diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 72638f8cd3..2115958509 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -97,6 +97,33 @@ jobs: ;; esac + spm: + name: "SPM" + runs-on: macos-15 + timeout-minutes: 30 + defaults: + run: + shell: bash + working-directory: flutter/example + strategy: + fail-fast: false + matrix: + target: [ios, macos] + + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@44ac965b96f18d999802d4b807e3256d5a3f9fa1 # pin@v2.16.0 + with: + channel: main + - run: flutter upgrade + - run: flutter config --enable-swift-package-manager + - name: Run on iOS + if: matrix.target == 'ios' + run: flutter build ios --no-codesign + - name: Run on macOS + if: matrix.target == 'macos' + run: flutter build macos + analyze: uses: ./.github/workflows/analyze.yml with: diff --git a/CHANGELOG.md b/CHANGELOG.md index d723e1f313..3c3dc79fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add SentryReplayQuality setting (`options.experimental.replay.quality`) ([#2582](https://github.com/getsentry/sentry-dart/pull/2582)) +- SPM Support ([#2280](https://github.com/getsentry/sentry-dart/pull/2280)) ### Dependencies diff --git a/flutter/.gitignore b/flutter/.gitignore index 30db743ffc..8ac84f58d5 100644 --- a/flutter/.gitignore +++ b/flutter/.gitignore @@ -10,4 +10,8 @@ build/ .cxx/ .vscode/launch.json +cocoa_bindings_temp + +ios/sentry_flutter/Package.resolved + temp diff --git a/flutter/ios/.gitignore b/flutter/ios/.gitignore index aa479fd3ce..d10a0d4773 100644 --- a/flutter/ios/.gitignore +++ b/flutter/ios/.gitignore @@ -34,4 +34,8 @@ Icon? .tags* /Flutter/Generated.xcconfig -/Flutter/flutter_export_environment.sh \ No newline at end of file +/Flutter/flutter_export_environment.sh + +.build +.swiftpm +Package.resolved diff --git a/flutter/ios/Assets/.gitkeep b/flutter/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/flutter/ios/Classes/SentryFlutterPlugin.h b/flutter/ios/Classes/SentryFlutterPlugin.h deleted file mode 100644 index 962c158028..0000000000 --- a/flutter/ios/Classes/SentryFlutterPlugin.h +++ /dev/null @@ -1,10 +0,0 @@ -#if TARGET_OS_IPHONE - #import -#else - #import -#endif - -#import - -@interface SentryFlutterPlugin : NSObject -@end diff --git a/flutter/ios/Classes/SentryFlutterPlugin.m b/flutter/ios/Classes/SentryFlutterPlugin.m deleted file mode 100644 index f541e31c0e..0000000000 --- a/flutter/ios/Classes/SentryFlutterPlugin.m +++ /dev/null @@ -1,15 +0,0 @@ -#import "SentryFlutterPlugin.h" -#if __has_include() -#import -#else -// Support project import fallback if the generated compatibility header -// is not copied when this plugin is created as a library. -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "sentry_flutter-Swift.h" -#endif - -@implementation SentryFlutterPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SentryFlutterPluginApple registerWithRegistrar:registrar]; -} -@end diff --git a/flutter/ios/sentry_flutter.podspec b/flutter/ios/sentry_flutter.podspec index b2b1c61549..adccd003f8 100644 --- a/flutter/ios/sentry_flutter.podspec +++ b/flutter/ios/sentry_flutter.podspec @@ -14,8 +14,8 @@ Sentry SDK for Flutter with support to native through sentry-cocoa. s.authors = "Sentry" s.source = { :git => "https://github.com/getsentry/sentry-dart.git", :tag => s.version.to_s } - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' + s.source_files = 'sentry_flutter/Sources/**/*' + s.public_header_files = 'sentry_flutter/Sources/**/*.h' s.dependency 'Sentry/HybridSDK', '8.43.0' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' diff --git a/flutter/ios/sentry_flutter/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/flutter/ios/sentry_flutter/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..919434a625 --- /dev/null +++ b/flutter/ios/sentry_flutter/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter/ios/sentry_flutter/Package.swift b/flutter/ios/sentry_flutter/Package.swift new file mode 100644 index 0000000000..12e38c34a8 --- /dev/null +++ b/flutter/ios/sentry_flutter/Package.swift @@ -0,0 +1,34 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "sentry_flutter", + platforms: [ + .iOS("12.0"), + .macOS("10.13") + ], + products: [ + .library(name: "sentry-flutter", targets: ["sentry_flutter", "sentry_flutter_objc"]) + ], + dependencies: [ + .package(url: "https://github.com/getsentry/sentry-cocoa", from: "8.43.0") + ], + targets: [ + .target( + name: "sentry_flutter", + dependencies: [ + "sentry_flutter_objc", + .product(name: "Sentry", package: "sentry-cocoa") + ] + ), + // SPM does not support mixed-language targets, so we need to move the ObjC files into a separate one + .target( + name: "sentry_flutter_objc", + dependencies: [ + .product(name: "Sentry", package: "sentry-cocoa") + ] + ) + ] +) diff --git a/flutter/ios/Classes/SentryFlutter.swift b/flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutter.swift similarity index 100% rename from flutter/ios/Classes/SentryFlutter.swift rename to flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutter.swift diff --git a/flutter/ios/Classes/SentryFlutterPluginApple.swift b/flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift similarity index 97% rename from flutter/ios/Classes/SentryFlutterPluginApple.swift rename to flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift index e739fd080f..c35b54b2be 100644 --- a/flutter/ios/Classes/SentryFlutterPluginApple.swift +++ b/flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift @@ -1,4 +1,10 @@ import Sentry + +#if SWIFT_PACKAGE +import Sentry._Hybrid +import sentry_flutter_objc +#endif + #if os(iOS) import Flutter import UIKit @@ -11,7 +17,7 @@ import CoreVideo // swiftlint:disable file_length function_body_length // swiftlint:disable:next type_body_length -public class SentryFlutterPluginApple: NSObject, FlutterPlugin { +public class SentryFlutterPlugin: NSObject, FlutterPlugin { private let channel: FlutterMethodChannel private static let nativeClientName = "sentry.cocoa.flutter" @@ -27,7 +33,7 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { let channel = FlutterMethodChannel(name: "sentry_flutter", binaryMessenger: registrar.messenger) #endif - let instance = SentryFlutterPluginApple(channel: channel) + let instance = SentryFlutterPlugin(channel: channel) registrar.addMethodCallDelegate(instance, channel: channel) } @@ -294,7 +300,7 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { } let version = PrivateSentrySDKOnly.getSdkVersionString() - PrivateSentrySDKOnly.setSdkName(SentryFlutterPluginApple.nativeClientName, andVersionString: version) + PrivateSentrySDKOnly.setSdkName(SentryFlutterPlugin.nativeClientName, andVersionString: version) // note : for now, in sentry-cocoa, beforeSend is not called before captureEnvelope options.beforeSend = { event in @@ -337,9 +343,9 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { #if canImport(UIKit) && !SENTRY_NO_UIKIT #if os(iOS) || os(tvOS) - let breadcrumbConverter = SentryFlutterReplayBreadcrumbConverter() - let screenshotProvider = SentryFlutterReplayScreenshotProvider(channel: self.channel) - PrivateSentrySDKOnly.configureSessionReplay(with: breadcrumbConverter, screenshotProvider: screenshotProvider) + let breadcrumbConverter = SentryFlutterReplayBreadcrumbConverter() + let screenshotProvider = SentryFlutterReplayScreenshotProvider(channel: self.channel) + PrivateSentrySDKOnly.configureSessionReplay(with: breadcrumbConverter, screenshotProvider: screenshotProvider) #endif #endif @@ -358,7 +364,7 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { if isValidSdk(sdk: sdk) { switch sdk["name"] as? String { - case SentryFlutterPluginApple.nativeClientName: + case SentryFlutterPlugin.nativeClientName: #if os(OSX) let origin = "mac" #elseif os(watchOS) @@ -473,7 +479,7 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { let isColdStart = appStartMeasurement.type == .cold let item: [String: Any] = [ - "pluginRegistrationTime": SentryFlutterPluginApple.pluginRegistrationTime, + "pluginRegistrationTime": SentryFlutterPlugin.pluginRegistrationTime, "appStartTime": appStartTime, "isColdStart": isColdStart, "nativeSpanTimes": nativeSpanTimes diff --git a/flutter/ios/Classes/SentryFlutterReplayBreadcrumbConverter.m b/flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayBreadcrumbConverter.m similarity index 97% rename from flutter/ios/Classes/SentryFlutterReplayBreadcrumbConverter.m rename to flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayBreadcrumbConverter.m index bde889b6bf..060bbbd80a 100644 --- a/flutter/ios/Classes/SentryFlutterReplayBreadcrumbConverter.m +++ b/flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayBreadcrumbConverter.m @@ -1,7 +1,11 @@ -#import "SentryFlutterReplayBreadcrumbConverter.h" +#import "include/SentryFlutterReplayBreadcrumbConverter.h" @import Sentry; +#if SWIFT_PACKAGE +@import Sentry._Hybrid; +#endif + #if SENTRY_TARGET_REPLAY_SUPPORTED @implementation SentryFlutterReplayBreadcrumbConverter { diff --git a/flutter/ios/Classes/SentryFlutterReplayScreenshotProvider.m b/flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayScreenshotProvider.m similarity index 98% rename from flutter/ios/Classes/SentryFlutterReplayScreenshotProvider.m rename to flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayScreenshotProvider.m index 7a6d8f2422..d8910fd98b 100644 --- a/flutter/ios/Classes/SentryFlutterReplayScreenshotProvider.m +++ b/flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayScreenshotProvider.m @@ -1,5 +1,9 @@ @import Sentry; +#if SWIFT_PACKAGE +@import Sentry._Hybrid; +#endif + #if SENTRY_TARGET_REPLAY_SUPPORTED #import "SentryFlutterReplayScreenshotProvider.h" #import diff --git a/flutter/ios/Classes/SentryFlutterReplayBreadcrumbConverter.h b/flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayBreadcrumbConverter.h similarity index 100% rename from flutter/ios/Classes/SentryFlutterReplayBreadcrumbConverter.h rename to flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayBreadcrumbConverter.h diff --git a/flutter/ios/Classes/SentryFlutterReplayScreenshotProvider.h b/flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayScreenshotProvider.h similarity index 100% rename from flutter/ios/Classes/SentryFlutterReplayScreenshotProvider.h rename to flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayScreenshotProvider.h diff --git a/flutter/macos/Classes/SentryFlutter.swift b/flutter/macos/Classes/SentryFlutter.swift deleted file mode 120000 index ea42d8c01c..0000000000 --- a/flutter/macos/Classes/SentryFlutter.swift +++ /dev/null @@ -1 +0,0 @@ -../../ios/Classes/SentryFlutter.swift \ No newline at end of file diff --git a/flutter/macos/Classes/SentryFlutterPlugin.h b/flutter/macos/Classes/SentryFlutterPlugin.h deleted file mode 120000 index 4e043862d2..0000000000 --- a/flutter/macos/Classes/SentryFlutterPlugin.h +++ /dev/null @@ -1 +0,0 @@ -../../ios/Classes/SentryFlutterPlugin.h \ No newline at end of file diff --git a/flutter/macos/Classes/SentryFlutterPlugin.m b/flutter/macos/Classes/SentryFlutterPlugin.m deleted file mode 120000 index 99905179b3..0000000000 --- a/flutter/macos/Classes/SentryFlutterPlugin.m +++ /dev/null @@ -1 +0,0 @@ -../../ios/Classes/SentryFlutterPlugin.m \ No newline at end of file diff --git a/flutter/macos/Classes/SentryFlutterPluginApple.swift b/flutter/macos/Classes/SentryFlutterPluginApple.swift deleted file mode 120000 index 1ac6c4f5be..0000000000 --- a/flutter/macos/Classes/SentryFlutterPluginApple.swift +++ /dev/null @@ -1 +0,0 @@ -../../ios/Classes/SentryFlutterPluginApple.swift \ No newline at end of file diff --git a/flutter/macos/sentry_flutter/Package.swift b/flutter/macos/sentry_flutter/Package.swift new file mode 120000 index 0000000000..fe0be63eb8 --- /dev/null +++ b/flutter/macos/sentry_flutter/Package.swift @@ -0,0 +1 @@ +../../ios/sentry_flutter/Package.swift \ No newline at end of file diff --git a/flutter/macos/sentry_flutter/Sources/sentry_flutter/SentryFlutter.swift b/flutter/macos/sentry_flutter/Sources/sentry_flutter/SentryFlutter.swift new file mode 120000 index 0000000000..9fbb9bb436 --- /dev/null +++ b/flutter/macos/sentry_flutter/Sources/sentry_flutter/SentryFlutter.swift @@ -0,0 +1 @@ +../../../../ios/sentry_flutter/Sources/sentry_flutter/SentryFlutter.swift \ No newline at end of file diff --git a/flutter/macos/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift b/flutter/macos/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift new file mode 120000 index 0000000000..30044f0967 --- /dev/null +++ b/flutter/macos/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift @@ -0,0 +1 @@ +../../../../ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift \ No newline at end of file diff --git a/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayBreadcrumbConverter.m b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayBreadcrumbConverter.m new file mode 120000 index 0000000000..a015af93e4 --- /dev/null +++ b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayBreadcrumbConverter.m @@ -0,0 +1 @@ +../../../../ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayBreadcrumbConverter.m \ No newline at end of file diff --git a/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayScreenshotProvider.m b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayScreenshotProvider.m new file mode 120000 index 0000000000..e0b12e88bb --- /dev/null +++ b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayScreenshotProvider.m @@ -0,0 +1 @@ +../../../../ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterReplayScreenshotProvider.m \ No newline at end of file diff --git a/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayBreadcrumbConverter.h b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayBreadcrumbConverter.h new file mode 120000 index 0000000000..a1f59a3999 --- /dev/null +++ b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayBreadcrumbConverter.h @@ -0,0 +1 @@ +../../../../../ios/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayBreadcrumbConverter.h \ No newline at end of file diff --git a/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayScreenshotProvider.h b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayScreenshotProvider.h new file mode 120000 index 0000000000..1c8b3852ca --- /dev/null +++ b/flutter/macos/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayScreenshotProvider.h @@ -0,0 +1 @@ +../../../../../ios/sentry_flutter/Sources/sentry_flutter_objc/include/SentryFlutterReplayScreenshotProvider.h \ No newline at end of file diff --git a/flutter/scripts/update-cocoa.sh b/flutter/scripts/update-cocoa.sh index 55f1c854db..21f9ef4fb3 100755 --- a/flutter/scripts/update-cocoa.sh +++ b/flutter/scripts/update-cocoa.sh @@ -2,25 +2,55 @@ set -euo pipefail cd $(dirname "$0")/../ios -file='sentry_flutter.podspec' -content=$(cat $file) -regex="('Sentry/HybridSDK', *)'([0-9\.]+(\-[a-z0-9\.]+)?)'" -if ! [[ $content =~ $regex ]]; then + +get_podspec_version() { + local file='sentry_flutter.podspec' + local content=$(cat $file) + regex="('Sentry/HybridSDK', *)'([0-9\.]+(\-[a-z0-9\.]+)?)'" + if ! [[ $content =~ $regex ]]; then + echo "Failed to find the plugin version in $file" + exit 1 + else + echo "${BASH_REMATCH[2]}" + fi +} + +set_podspec_version() { + local file='sentry_flutter.podspec' + local content=$(cat $file) + regex="('Sentry/HybridSDK', *)'([0-9\.]+(\-[a-z0-9\.]+)?)'" + if ! [[ $content =~ $regex ]]; then + echo "Failed to find the plugin version in $file" + exit 1 + else + newValue="${BASH_REMATCH[1]}'$1'" + echo "${content/${BASH_REMATCH[0]}/$newValue}" >$file + fi +} + +set_spm_version() { + local file='sentry_flutter/Package.swift' + local content=$(cat $file) + regex="(url: *['\"]https://github.com/getsentry/sentry-cocoa['\"], *from: *)['\"]([0-9\.]+(-[a-z0-9\.]+)?)['\"]" + if ! [[ $content =~ $regex ]]; then echo "Failed to find the plugin version in $file" exit 1 -fi + else + newValue="${BASH_REMATCH[1]}\"$1\"" + echo "${content/${BASH_REMATCH[0]}/$newValue}" >$file + fi +} case $1 in get-version) - echo ${BASH_REMATCH[2]} + echo $(get_podspec_version) ;; get-repo) echo "https://github.com/getsentry/sentry-cocoa.git" ;; set-version) - newValue="${BASH_REMATCH[1]}'$2'" - echo "${content/${BASH_REMATCH[0]}/$newValue}" >$file - ../scripts/generate-cocoa-bindings.sh "$2" + set_podspec_version "$2" + set_spm_version "$2" ;; *) echo "Unknown argument $1"