UNPKG

react-native-marketingcloudsdk

Version:

A React Native component to access the native Salesforce Marketing Cloud MobilePush SDKs

440 lines (352 loc) 21.9 kB
# Enable Push for iOS 1. Enable push notifications in your target’s Capabilities settings in Xcode. ![push enablement](/assets/SDKConfigure6.png) 2. Set your AppDelegate class to adhere to the `UNUserNotificationCenterDelegate` protocol. ```objc //Other imports... #import <UserNotifications/UserNotifications.h> #import <MarketingCloudSDK/MarketingCloudSDK.h> #import <SFMCSDK/SFMCSDK.h> @interface AppDelegate : RCTAppDelegate<UNUserNotificationCenterDelegate, SFMCSdkURLHandlingDelegate> ``` 3. Extend the SDK configuration code outlined in [Configure the SDK](./README.md#2-configure-the-sdk-in-your-appdelegatem-class) to add support for push registration. ```objc @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //RN setup self.moduleName = @"example"; // You can add your custom initial props in the dictionary below. // They will be passed down to the ViewController used by React Native. self.initialProps = @{}; // Configure the SFMC sdk ... PushConfigBuilder *pushConfigBuilder = [[PushConfigBuilder alloc] initWithAppId:@"{MC_APP_ID}"]; [pushConfigBuilder setAccessToken:@"{MC_ACCESS_TOKEN}"]; [pushConfigBuilder setMarketingCloudServerUrl:[NSURL URLWithString:@"{MC_APP_SERVER_URL}"]]; [pushConfigBuilder setMid:@"MC_MID"]; [pushConfigBuilder setAnalyticsEnabled:YES]; [SFMCSdk initializeSdk:[[[SFMCSdkConfigBuilder new] setPushWithConfig:[pushConfigBuilder build] onCompletion:^(SFMCSdkOperationResult result) { if (result == SFMCSdkOperationResultSuccess) { [self pushSetup]; } else { // SFMC sdk configuration failed. NSLog(@"SFMC sdk configuration failed."); } }] build]]; // ... The rest of the didFinishLaunchingWithOptions method return [super application:application didFinishLaunchingWithOptions:launchOptions]; } - (void)pushSetup { // AppDelegate adheres to the SFMCSdkURLHandlingDelegate protocol // and handles URLs passed back from the SDK in `sfmc_handleURL`. // For more information, see https://salesforce-marketingcloud.github.io/MarketingCloudSDK-iOS/sdk-implementation/implementation-urlhandling.html [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) { [mp setURLHandlingDelegate:self]; }]; dispatch_async(dispatch_get_main_queue(), ^{ // set the UNUserNotificationCenter delegate - the delegate must be set here in // didFinishLaunchingWithOptions [UNUserNotificationCenter currentNotificationCenter].delegate = self; [[UIApplication sharedApplication] registerForRemoteNotifications]; [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge completionHandler:^(BOOL granted, NSError *_Nullable error) { if (error == nil) { if (granted == YES) { NSLog(@"User granted permission"); } } }]; }); } - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) { [mp setDeviceToken:deviceToken]; }]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { os_log_debug(OS_LOG_DEFAULT, "didFailToRegisterForRemoteNotificationsWithError = %@", error); } // The method will be called on the delegate when the user responded to the notification by opening // the application, dismissing the notification or choosing a UNNotificationAction. The delegate // must be set before the application returns from applicationDidFinishLaunching:. - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { // tell the MarketingCloudSDK about the notification [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) { [mp setNotificationResponse:response]; }]; if (completionHandler != nil) { completionHandler(); } } - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler: (void (^)(UNNotificationPresentationOptions options))completionHandler { NSLog(@"User Info : %@", notification.request.content.userInfo); completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge); } // This method is REQUIRED for correct functionality of the SDK. // This method will be called on the delegate when the application receives a silent push - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) { [mp setNotificationUserInfo:userInfo]; }]; completionHandler(UIBackgroundFetchResultNewData); } //URL Handling - (void)sfmc_handleURL:(NSURL * _Nonnull)url type:(NSString * _Nonnull)type { if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) { if (success) { NSLog(@"url %@ opened successfully", url); } else { NSLog(@"url %@ could not be opened", url); } }]; } } @end ``` 4. **Integrate the MobilePush Extension SDK:** The MobilePush SDK version 9.0.0 introduces two push notification features — Push Delivery events and support for Carousel template. To function properly, these features require iOS [Service Extension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension) and [Content Extension](https://developer.apple.com/documentation/usernotificationsui/unnotificationcontentextension) added as additional targets in the main app. Then, integrate the MobilePush iOS Extension SDK MCExtensionSDK. >Implementing the **Service Extension** is required to enable **Push Delivery events**. >Implementing both the **Service Extension** and **Content Extension** is required to support **Carousel template-based Push Notifications**. #### 1. Configure the Service Extension This section will guide you through setting up and configuring the Notification Service Extension for use with the MobilePush iOS Extension SDK. - [Add a Service Extension Target](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#add-a-service-extension-target) - Integrate the Extension SDK with the Service Extension: Add the SDK as a dependency in your app's Podfile, follow the instructions for [Adding pods to an Xcode project](https://guides.cocoapods.org/using/using-cocoapods.html) on the CocoaPods documentation site. ![podfile changes](/assets/SDKPodfile.png) After the installation process, open the `.xcworkspace` file created by CocoaPods using Xcode. **__Avoid opening .xcodeproj directly. Opening a project file instead of a workspace can lead to errors.__** - [Inherit from SFMCNotificationService](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#inherit-from-sfmcnotificationservice) - [Additional Configuration](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#additional-configuration-options) #### 2. Configure the Content Extension This section will guide you through setting up and configuring the Notification Content Extension for use with the MobilePush iOS Extension SDK. - [Add a Content Extension Target](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#add-a-content-extension-target) - Integrate the Extension SDK with the Content Extension: The process for integrating the MCExtensionSDK into your Content Extension mirrors the steps taken for integration with your Service Extension. - [Inherit from SFMCNotificationViewController](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#inherit-from-sfmcnotificationviewcontroller) - [Project and Info.plist Configuration](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#project-and-infoplist-configuration) - [Additional Configuration](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#additional-configuration) #### 3. [Enable App Groups Capability](https://developer.salesforce.com/docs/marketing/mobilepush/guide/ios-extension-sdk-integration.html#enable-app-groups-capability) 5. **Enable Rich Notifications:** Rich notifications include images, videos, titles and subtitles from the MobilePush app, and mutable content. Mutable content can include personalization in the title, subtitle, or body of your message. **Create a Notification Service Extension** Skip the setup steps if you've already integrated Notification Service Extension during the [MobilePush Extension SDK integration](#4-integrate-the-mobilepush-extension-sdk:) and refer to the [sample code for integration with Extension SDK](#with-Extension-SDK-Integration). 1. In Xcode, click **File** 2. Click **New** 3. Click **Target** 4. Select Notification Service Extension 5. Name and save the new extension > The Notification Target must be signed with the same Xcode Managed Profile as the main project. This service extension checks for a “_mediaUrl” element in request.content.userInfo. If found, the extension attempts to download the media from the URL , creates a thumbnail-size version, and then adds the attachment. The service extension also checks for a ““_mediaAlt” element in request.content.userInfo. If found, the service extension uses the element for the body text if there are any problems downloading or creating the media attachment. A service extension can timeout when it is unable to download. In this code sample, the service extension delivers the original content with the body text changed to the value in “_mediaAlt”. #### **<ins>Without Extension SDK Integration</ins>** ```objc #import <CoreGraphics/CoreGraphics.h> #import "NotificationService.h" @interface NotificationService () @property(nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property(nonatomic, strong) UNMutableNotificationContent *modifiedNotificationContent; @end @implementation NotificationService - (UNNotificationAttachment *)createMediaAttachment:(NSURL *)localMediaUrl { // options: specify what cropping rectangle of the media to use for a thumbnail // whether the thumbnail is hidden or not UNNotificationAttachment *mediaAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"attachmentIdentifier" URL:localMediaUrl options:@{ UNNotificationAttachmentOptionsThumbnailClippingRectKey : (NSDictionary *)CFBridgingRelease( CGRectCreateDictionaryRepresentation(CGRectZero)), UNNotificationAttachmentOptionsThumbnailHiddenKey : @NO } error:nil]; return mediaAttachment; } - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler { // save the completion handler we will call back later self.contentHandler = contentHandler; // make a copy of the notification so we can change it self.modifiedNotificationContent = [request.content mutableCopy]; // alternative text to display if there are any issues loading the media URL NSString *mediaAltText = request.content.userInfo[@"_mediaAlt"]; // does the payload contains a remote URL to download or a local URL? NSString *mediaUrlString = request.content.userInfo[@"_mediaUrl"]; NSURL *mediaUrl = [NSURL URLWithString:mediaUrlString]; // if we have a URL, try to download media (i.e., // https://media.giphy.com/media/3oz8xJBbCpzG9byZmU/giphy.gif) if (mediaUrl != nil) { // create a session to handle downloading of the URL NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; // start a download task to handle the download of the media __weak __typeof__(self) weakSelf = self; [[session downloadTaskWithURL:mediaUrl completionHandler:^(NSURL *_Nullable location, NSURLResponse *_Nullable response, NSError *_Nullable error) { BOOL useAlternateText = YES; // if the download succeeded, save it locally and then make an attachment if (error == nil) { if (200 <= ((NSHTTPURLResponse *)response).statusCode && ((NSHTTPURLResponse *)response).statusCode <= 299) { // download was successful, attempt save the media file NSURL *localMediaUrl = [NSURL fileURLWithPath:[location.path stringByAppendingString:mediaUrl .lastPathComponent]]; // remove any existing file with the same name [[NSFileManager defaultManager] removeItemAtURL:localMediaUrl error:nil]; // move the downloaded file from the temporary location to a new file if ([[NSFileManager defaultManager] moveItemAtURL:location toURL:localMediaUrl error:nil] == YES) { // create an attachment with the new file UNNotificationAttachment *mediaAttachment = [weakSelf createMediaAttachment:localMediaUrl]; // if no problems creating the attachment, we can use it if (mediaAttachment != nil) { // set the media to display in the notification weakSelf.modifiedNotificationContent.attachments = @[ mediaAttachment ]; // everything is ok useAlternateText = NO; } } } } // if any problems creating the attachment, use the alternate text if provided if ((useAlternateText == YES) && (mediaAltText != nil)) { weakSelf.modifiedNotificationContent.body = mediaAltText; } // tell the OS we are done and here is the new content weakSelf.contentHandler(weakSelf.modifiedNotificationContent); }] resume]; } else { // see if the media URL is for a local file (i.e., file://movie.mp4) BOOL useAlternateText = YES; if (mediaUrlString != nil) { // attempt to create a URL to a file in local storage NSURL *localMediaUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:mediaUrlString.lastPathComponent .stringByDeletingLastPathComponent ofType:mediaUrlString.pathExtension]]; // is the URL a local file URL? if (localMediaUrl != nil && localMediaUrl.isFileURL == YES) { // create an attachment with the local media UNNotificationAttachment *mediaAttachment = [self createMediaAttachment:localMediaUrl]; // if no problems creating the attachment, we can use it if (mediaAttachment != nil) { // set the media to display in the notification self.modifiedNotificationContent.attachments = @[ mediaAttachment ]; // everything is ok useAlternateText = NO; } } } // if any problems creating the attachment, use the alternate text if provided if ((useAlternateText == YES) && (mediaAltText != nil)) { self.modifiedNotificationContent.body = mediaAltText; } // tell the OS we are done and here is the new content contentHandler(self.modifiedNotificationContent); } } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the // original push payload will be used. // we took too long to download the media URL, use the alternate text if provided NSString *mediaAltText = self.modifiedNotificationContent.userInfo[@"_mediaAlt"]; if (mediaAltText != nil) { self.modifiedNotificationContent.body = mediaAltText; } // tell the OS we are done and here is the new content self.contentHandler(self.modifiedNotificationContent); } @end ``` #### **<ins>With Extension SDK Integration</ins>** ```objc #import "NotificationService.h" @implementation NotificationService // Provide SFNotificationServiceConfig configuration - (SFMCNotificationServiceConfig *)sfmcProvideConfig { SFMCExtensionSdkLogLevel logLevel = SFMCExtensionSdkLogLevelNone; #if DEBUG logLevel = SFMCExtensionSdkLogLevelDebug; #endif return [[SFMCNotificationServiceConfig alloc] initWithLogLevel: logLevel]; } // Custom processing when notification is received -(void)sfmcDidReceiveRequest:(UNNotificationRequest *)request mutableContent:(UNMutableNotificationContent *)mutableContent withContentHandler:(void (^)(NSDictionary * _Nullable))contentHandler { [self addMediaToContent:mutableContent completion:^{ contentHandler(nil); }]; } // Download and attach media - (void)addMediaToContent:(UNMutableNotificationContent *)mutableContent completion:(void (^)(void))completion { NSString *mediaUrlString = mutableContent.userInfo[@"_mediaUrl"]; if (mediaUrlString == nil || mediaUrlString.length == 0) { completion(); return; } NSURL *mediaUrl = [NSURL URLWithString:mediaUrlString]; if (!mediaUrl) { completion(); return; } NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:mediaUrl completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { completion(); return; } if (!location || ![response isKindOfClass:[NSHTTPURLResponse class]]) { completion(); return; } NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; if (statusCode < 200 || statusCode > 299) { completion(); return; } NSString *fileName = mediaUrl.lastPathComponent; NSString *destinationPath = [[location.path stringByAppendingString:fileName] copy]; NSURL *localMediaUrl = [NSURL fileURLWithPath:destinationPath]; [[NSFileManager defaultManager] removeItemAtURL:localMediaUrl error:nil]; NSError *fileError; [[NSFileManager defaultManager] moveItemAtURL:location toURL:localMediaUrl error:&fileError]; if (fileError) { completion(); return; } UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"SomeAttachmentId" URL:localMediaUrl options:nil error:nil]; if (attachment) { mutableContent.attachments = @[attachment]; } completion(); }]; [downloadTask resume]; } @end ```