diff --git a/CHANGELOG.md b/CHANGELOG.md index f9dd059..d89c0ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## 4.0.0 + +> Starting with this release, this SDK will use [Semantic Versioning](https://semver.org/). + +##### Breaking +- Fixes the behavior in the iOS bridge introduced in version `3.0.0` when logging clicks for in-app messages and content cards. Calling `logClick` now only sends a click event for metrics, instead of both sending a click event as well as redirecting to the associated `url` field. + - For instance, to log a content card click and redirect to a URL, you will need two commands: + ``` + braze.logContentCardClicked(contentCard); + + // Your own custom implementation + Linking.openUrl(contentCard.url); + ``` + - This brings the iOS behavior to match version `2.x` and bring parity with Android's behavior. +- Removes `setBrazeInAppMessageCallback()` and `setBrazeContentCardsCallback()` in favor of subscribing via streams. + - Reference our [sample app](https://github.com/braze-inc/braze-flutter-sdk/blob/master/example/lib/main.dart) for an example on how to use [`subscribeToInAppMessages()`](https://www.braze.com/docs/developer_guide/platform_integration_guides/flutter/inapp_messages/#receiving-in-app-message-data) or [`subscribeToContentCards()`](https://www.braze.com/docs/developer_guide/platform_integration_guides/flutter/content_cards/#receiving-content-card-data). + +##### Changed +- The native Android bridge uses [Braze Android SDK 24.3.0](https://github.com/braze-inc/braze-android-sdk/blob/master/CHANGELOG.md#2430). +- The native iOS bridge uses [Braze iOS SDK 5.11.2](https://github.com/braze-inc/braze-swift-sdk/blob/main/CHANGELOG.md#5112). +- Improves behavior when using `replayCallbacksConfigKey` alongside having subscriptions to in-app messages or content cards via streams. + ## 3.1.0 ##### Breaking @@ -57,7 +79,7 @@ ##### Changed - Updates the iOS layer to use Swift. `BrazePlugin.h` and `BrazePlugin.m` are now consolidated to `BrazePlugin.swift`. -- Deprecates `setBrazeInAppMessageCallback()` and `setBrazeContentCardsCallback()` in favor of the subscribing via streams. +- Deprecates `setBrazeInAppMessageCallback()` and `setBrazeContentCardsCallback()` in favor of subscribing via streams. ## 2.5.0 diff --git a/android/.idea/.name b/android/.idea/.name index 7316a8e..58432f7 100644 --- a/android/.idea/.name +++ b/android/.idea/.name @@ -1 +1 @@ -_android \ No newline at end of file +braze_plugin \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index e12bbb3..92683c8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -52,5 +52,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.appboy:android-sdk-ui:24.2.0" + implementation "com.appboy:android-sdk-ui:24.3.0" } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 2daf368..3556f09 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -80,7 +80,7 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'androidx.multidex:multidex:2.0.0' - implementation "com.appboy:android-sdk-ui:24.2.0" + implementation "com.appboy:android-sdk-ui:24.3.0" implementation "com.google.firebase:firebase-messaging:+" } apply plugin: 'com.google.gms.google-services' diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..9625e10 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index b991291..8e02440 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -215,6 +215,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -229,6 +230,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 20c2d2c..3a8e47e 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -12,8 +12,6 @@ let brazeEndpoint = "sondheim.appboy.com" @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate, BrazeInAppMessageUIDelegate { - static var braze: Braze? = nil - // The subscription needs to be retained to be active var contentCardsSubscription: Braze.Cancellable? @@ -28,7 +26,7 @@ let brazeEndpoint = "sondheim.appboy.com" configuration.sessionTimeout = 1 configuration.triggerMinimumTimeInterval = 0 configuration.location.automaticLocationCollection = true - configuration.location.brazeLocation = BrazeLocation() + configuration.location.brazeLocationProvider = BrazeLocationProvider() configuration.logger.level = .debug let braze = BrazePlugin.initBraze(configuration) diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index dd3751e..9ca9d23 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -50,5 +50,7 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index 7faf1c4..373300f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -35,6 +35,10 @@ class BrazeFunctionsState extends State { StreamSubscription inAppMessageStreamSubscription; StreamSubscription contentCardsStreamSubscription; + // Change to `true` to automatically log clicks, button clicks, + // and impressions for in-app messages and content cards. + final automaticallyInteract = false; + void initState() { _braze = new BrazePlugin(customConfigs: {replayCallbacksConfigKey: true}); @@ -239,20 +243,6 @@ class BrazeFunctionsState extends State { }, ), SectionHeader("In-app Messages"), - TextButton( - child: const Text('SET IN-APP MESSAGE CALLBACK'), - onPressed: () { - // ignore: deprecated_member_use - _braze.setBrazeInAppMessageCallback( - (BrazeInAppMessage inAppMessage) { - _inAppMessageReceived(inAppMessage, prefix: "CALLBACK"); - }); - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text("In-app message callback set. " - "In-app message data will appear in snackbars."), - )); - }, - ), TextButton( child: const Text('SUBSCRIBE VIA IN-APP MESSAGE STREAM'), onPressed: () { @@ -283,20 +273,6 @@ class BrazeFunctionsState extends State { _braze.launchContentCards(); }, ), - TextButton( - child: const Text('SET CONTENT CARDS CALLBACK'), - onPressed: () { - // ignore: deprecated_member_use - _braze.setBrazeContentCardsCallback( - (List contentCards) { - _contentCardsReceived(contentCards, prefix: "CALLBACK"); - }); - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text("Content Cards Callback set. " - "Content Card data will appear in snackbars."), - )); - }, - ), TextButton( child: const Text('SUBSCRIBE VIA CONTENT CARDS STREAM'), onPressed: () { @@ -439,8 +415,7 @@ class BrazeFunctionsState extends State { ); } - void _inAppMessageReceived(BrazeInAppMessage inAppMessage, - {String prefix, bool automaticallyInteract = false}) { + void _inAppMessageReceived(BrazeInAppMessage inAppMessage, {String prefix}) { print("[$prefix] Received message: ${inAppMessage.toString()}"); ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: @@ -460,7 +435,7 @@ class BrazeFunctionsState extends State { } void _contentCardsReceived(List contentCards, - {String prefix, bool automaticallyInteract = false}) { + {String prefix}) { if (contentCards.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text("Empty Content Cards update received."), diff --git a/ios/Classes/BrazePlugin.swift b/ios/Classes/BrazePlugin.swift index 8287ef9..7f328b1 100644 --- a/ios/Classes/BrazePlugin.swift +++ b/ios/Classes/BrazePlugin.swift @@ -9,9 +9,6 @@ public class BrazePlugin: NSObject, FlutterPlugin, BrazeDelegate { public static var braze: Braze? = nil - private static var inAppMessageIdsToContexts: [String: Braze.InAppMessage.Context] = [:] - private static var contentCardIdsToContexts: [String: Braze.ContentCard.Context] = [:] - public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "braze_plugin", binaryMessenger: registrar.messenger()) let instance = BrazePlugin() @@ -481,11 +478,6 @@ public class BrazePlugin: NSObject, FlutterPlugin, BrazeDelegate { do { var inAppMessage: Braze.InAppMessage = try Braze.InAppMessage.init(inAppMessageRaw) - - // TODO: New context is being allocated each time, so can log duplicate impressions - let context = Braze.InAppMessage.Context(message: inAppMessage, using: braze) - inAppMessage.context = context - return inAppMessage } catch { print("Error parsing in-app message from jsonString: \(jsonString), error: \(error)") @@ -499,11 +491,6 @@ public class BrazePlugin: NSObject, FlutterPlugin, BrazeDelegate { do { var contentCard: Braze.ContentCard = try Braze.ContentCard.init(contentCardRaw) - - // TODO: New context is being allocated each time, so can log duplicate impressions - let context = Braze.ContentCard.Context(card: contentCard, using: braze) - contentCard.context = context - return contentCard } catch { print("Error parsing Content Card from jsonString: \(jsonString), error: \(error)") diff --git a/ios/braze_plugin.podspec b/ios/braze_plugin.podspec index e39796e..149085b 100644 --- a/ios/braze_plugin.podspec +++ b/ios/braze_plugin.podspec @@ -14,9 +14,9 @@ Pod::Spec.new do |s| s.static_framework = true s.dependency 'Flutter' - s.dependency 'BrazeKit', '~> 5.9.0' - s.dependency 'BrazeLocation', '~> 5.9.0' - s.dependency 'BrazeUI', '~> 5.9.0' + s.dependency 'BrazeKit', '~> 5.11.2' + s.dependency 'BrazeLocation', '~> 5.11.2' + s.dependency 'BrazeUI', '~> 5.11.2' s.ios.deployment_target = '11.0' end diff --git a/lib/braze_plugin.dart b/lib/braze_plugin.dart index 0939b0b..f02b2d8 100644 --- a/lib/braze_plugin.dart +++ b/lib/braze_plugin.dart @@ -10,10 +10,8 @@ class BrazePlugin { Map? _brazeCustomConfigs; Function(BrazeSdkAuthenticationError)? _brazeSdkAuthenticationErrorHandler; - Function(BrazeInAppMessage)? _brazeInAppMessageHandler; + // To be used alongside `replayCallbacksConfigKey` final List _queuedInAppMessages = []; - - Function(List)? _brazeContentCardHandler; final List _queuedContentCards = []; /// Broadcast stream to listen for in-app messages. @@ -24,15 +22,26 @@ class BrazePlugin { StreamController> contentCardsStreamController = StreamController>.broadcast(); + /// The plugin used to interface with all Braze APIs with optional parameters + /// specific customization. + /// + /// The [inAppMessageHandler] and [contentCardsHandler] can subscribe to + /// their respective streams at plugin initialization. These can also be + /// subscribed at a later time after initialization BrazePlugin( {Function(BrazeInAppMessage)? inAppMessageHandler, Function(BrazeSdkAuthenticationError)? brazeSdkAuthenticationErrorHandler, Function(List)? contentCardsHandler, Map? customConfigs}) { - _brazeInAppMessageHandler = inAppMessageHandler; - _brazeSdkAuthenticationErrorHandler = brazeSdkAuthenticationErrorHandler; - _brazeContentCardHandler = contentCardsHandler; _brazeCustomConfigs = customConfigs; + _brazeSdkAuthenticationErrorHandler = brazeSdkAuthenticationErrorHandler; + + if (inAppMessageHandler != null) { + subscribeToInAppMessages(inAppMessageHandler); + } + if (contentCardsHandler != null) { + subscribeToContentCards(contentCardsHandler); + } // Called after setting up plugin settings _channel.setMethodCallHandler(_handleBrazeData); @@ -70,19 +79,6 @@ class BrazePlugin { return subscription; } - /// Sets a callback to receive in-app message data from Braze. - @Deprecated( - 'Use subscribeToInAppMessages(void onEvent(List contentCard)) instead.') - void setBrazeInAppMessageCallback(Function(BrazeInAppMessage) callback) { - _brazeInAppMessageHandler = callback; - - if (_replayCallbacksConfigEnabled() && _queuedInAppMessages.isNotEmpty) { - print("Replaying callback on previously queued Braze in-app messages."); - _queuedInAppMessages.forEach((message) => callback(message)); - _queuedInAppMessages.clear(); - } - } - /// Sets a callback to receive in-app message data from Braze. void setBrazeSdkAuthenticationErrorCallback( Function(BrazeSdkAuthenticationError) callback) { @@ -90,19 +86,6 @@ class BrazePlugin { _brazeSdkAuthenticationErrorHandler = callback; } - /// Sets a callback to receive Content Card data from Braze. - @Deprecated( - 'Use subscribeToContentCards(void onEvent(List contentCard)) instead.') - void setBrazeContentCardsCallback(Function(List) callback) { - _brazeContentCardHandler = callback; - - if (_replayCallbacksConfigEnabled() && _queuedContentCards.isNotEmpty) { - print("Replaying callback on previously queued Braze content cards."); - callback(_queuedContentCards); - _queuedContentCards.clear(); - } - } - /// Changes the current Braze userId. /// If [sdkAuthSignature] is present, passes that token to the native layer. /// @@ -515,15 +498,14 @@ class BrazePlugin { return Future.value(); } final inAppMessage = BrazeInAppMessage(inAppMessageString); - if (_brazeInAppMessageHandler != null) { - _brazeInAppMessageHandler!(inAppMessage); - } else if (_replayCallbacksConfigEnabled()) { - print("Braze in-app message callback not present. Adding to queue."); + if (inAppMessageStreamController.hasListener) { + inAppMessageStreamController.add(inAppMessage); + } else { + print( + "Braze in-app message subscription not present. Adding to queue."); _queuedInAppMessages.add(inAppMessage); } - // Add valid in-app message to the stream. - inAppMessageStreamController.add(inAppMessage); return Future.value(); case "handleBrazeContentCards": @@ -533,17 +515,15 @@ class BrazePlugin { brazeCards.add(BrazeContentCard(card)); } - if (_brazeContentCardHandler != null) { - _brazeContentCardHandler!(brazeCards); - } else if (_replayCallbacksConfigEnabled()) { + if (contentCardsStreamController.hasListener) { + contentCardsStreamController.add(brazeCards); + } else { print( - "Braze content card callback not present. Removing any queued cards and adding only the recent refresh."); + "Braze content card subscription not present. Removing any queued cards and adding only the recent refresh."); _queuedContentCards.clear(); _queuedContentCards.addAll(brazeCards); } - // Add valid list of content cards to the stream. - contentCardsStreamController.add(brazeCards); return Future.value(); case "handleSdkAuthenticationError": diff --git a/pubspec.yaml b/pubspec.yaml index 84f6d50..0658ea5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: braze_plugin description: This is the Braze plugin for Flutter. Effective marketing automation is an essential part of successfully scaling and managing your business. -version: 3.1.0 +version: 4.0.0 homepage: https://www.braze.com/ repository: https://github.com/braze-inc/braze-flutter-sdk @@ -24,3 +24,8 @@ flutter: pluginClass: BrazePlugin ios: pluginClass: BrazePlugin + +# Ignore warnings in the example app during package upload +false_secrets: + - /example/android/app/google-services.json + - /example/ios/Runner/GoogleService-Info.plist \ No newline at end of file diff --git a/test/braze_plugin_test.dart b/test/braze_plugin_test.dart index 176507e..f25f240 100644 --- a/test/braze_plugin_test.dart +++ b/test/braze_plugin_test.dart @@ -100,7 +100,6 @@ void main() { }); test('should include isControl field', () { - BrazePlugin _braze = new BrazePlugin(); String _data = '{"tp":"control"}'; BrazeContentCard _contentCard = new BrazeContentCard(_data); expect(_contentCard.isControl, equals(true));