Configure the Flutter Plugin for iOS Apps

After you install and configure the Flutter plugin for the Salesforce Engagement SDK, configure the plugin to enable push support for the iOS platform.

Prerequisites 

Install the dependencies for the SDK. See Integrate the SDK for iOS.

Install the Flutter plugin. See Marketing Cloud Flutter Plugin.

Enable Push Notifications 

Enable push notifications in the Capabilities settings for your target in Xcode.

Enable Push

Update the AppDelegate 

  1. Navigate to the YOUR_APP/ios directory and open Runner.xcworkspace.

  2. To configure the SDK and enable push, update AppDelegate.

View Swift code example
1//AppDelegate.swift
2
3  import UIKit
4  import Flutter
5  // The SFMCSDK module is included in the dependency packages for the SDK. Don't install it
6  // separately or list it in your podfile.
7  import SFMCSDK
8  import MarketingCloudSDK
9
10  func setupMobilePush() {
11    // Make sure to dispatch this to the main thread, as UNUserNotificationCenter will
12    // present UI.
13    DispatchQueue.main.async {
14      // Set the UNUserNotificationCenterDelegate to a class adhering to thie protocol.
15      // In this exmple, the AppDelegate class adheres to the protocol (see below)
16      // and handles Notification Center delegate methods from iOS.
17      UNUserNotificationCenter.current().delegate = self
18
19      // Request authorization from the user for push notification alerts.
20      UNUserNotificationCenter.current().requestAuthorization(
21        options: [.alert, .sound, .badge],
22        completionHandler: {(_ granted: Bool, _ error: Error?) -> Void in
23          if error == nil {
24            if granted == true {
25              // Your application may want to do something specific if the user has granted
26              // authorization for the notification types specified; it would be done here.
27            }
28          }
29        })
30
31      // Your application should register for remote notifications each time your application
32      // launches to make sure that the push token for silent push is updated, if necessary.
33
34      // Registering in this manner doesn't mean that a user sees a notification. It only
35      // means that the application receives a unique push token from iOS.
36      UIApplication.shared.registerForRemoteNotifications()
37    }
38  }
39
40  @UIApplicationMain
41  @objc class AppDelegate: FlutterAppDelegate {
42
43    // The appID, accessToken, and appEndpoint are required values for configuring the SDK. Obtain
44    // these values from your app.
45    let appID = YOUR_APP_ID
46    let accessToken = YOUR_ACCESS_TOKEN
47    let appEndpointURL = YOUR_APP_ENDPOINT
48    let mid = YOUR_ACCOUNT_MID
49    // Define the features your app uses.
50    let analytics = true
51
52
53    override func application(
54        _ application: UIApplication,
55        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
56    ) -> Bool {
57        GeneratedPluginRegistrant.register(with: self)
58
59            // Required: tell the MarketingCloudSDK about the notification. The SDK collects
60            // analytics and processes the notification on behalf of your application.
61            SFMCSdk.requestPushSdk { mp in
62                mp.setNotificationResponse(response)
63            }
64
65        // rest of the didFinishLaunchingWithOptions method...
66        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
67        }
68
69        completionHandler(.alert)
70  }
View Objective-C code example
1//AppDelegate.h
2
3  #import <Flutter/Flutter.h>
4  #import <UIKit/UIKit.h>
5  #import <UserNotifications/UserNotifications.h>
6  // The SFMCSDK module is included in the dependency packages for the SDK. Don't install it
7  // separately or list it in your podfile.
8  #import <SFMCSDK/SFMCSDK.h>
9  //Other imports...
10
11  @interface AppDelegate : FlutterAppDelegate<UNUserNotificationCenterDelegate>
12
13  @end
14
15  //AppDelegate.m
16
17  #import "AppDelegate.h"
18  #import "GeneratedPluginRegistrant.h"
19  #import <MarketingCloudSDK/MarketingCloudSDK.h>
20  //Other imports...
21
22  @implementation AppDelegate
23
24  - (BOOL)application:(UIApplication *)application
25      didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
26
27      //Flutter setup
28      [GeneratedPluginRegistrant registerWithRegistry:self];
29      // Override point for customization after application launch.
30
31      // Use the Push Config Builder to configure the Mobile Push Module. This gives you the
32      // maximum flexibility in SDK configuration. The builder lets you configure the module
33      // parameters at runtime.
34      PushConfigBuilder *pushConfigBuilder =
35        [[PushConfigBuilder alloc] initWithAppId:@"{MC_APP_ID}"];
36      [pushConfigBuilder setAccessToken:@"{MC_ACCESS_TOKEN}"];
37      [pushConfigBuilder setMarketingCloudServerUrl:
38        [NSURL URLWithString:@"{MC_APP_SERVER_URL}"]];
39      [pushConfigBuilder setMid:@"MC_MID"];
40      [pushConfigBuilder setAnalyticsEnabled:YES];
41
42
43      // Once you’ve created the mobile push configuration, intialize the SDK.
44      [SFMCSdk initializeSdk:[[[SFMCSdkConfigBuilder new]
45        setPushWithConfig:[pushConfigBuilder build]
46        onCompletion:^(SFMCSdkOperationResult result) {
47          if (result == SFMCSdkOperationResultSuccess) {
48              // module is fully configured and ready for use
49              [self pushSetup];
50          } else {
51              NSLog(@"SFMC sdk configuration failed.");
52          }
53      }] build]];
54
55      // rest of the didFinishLaunchingWithOptions method...
56      return [super application:application didFinishLaunchingWithOptions:launchOptions];
57  }
58
59  - (void)pushSetup {
60      // Make sure to dispatch this to the main thread, as UNUserNotificationCenter will
61      // present UI.
62      dispatch_async(dispatch_get_main_queue(), ^{
63          // Set the UNUserNotificationCenterDelegate to a class adhering to thie protocol.
64          // In this exmple, the AppDelegate class adheres to the protocol (see below)
65          // and handles Notification Center delegate methods from iOS.
66          [UNUserNotificationCenter currentNotificationCenter].delegate = self;
67
68          // Your application should register for remote notifications each time your
69          // application launches to make sure that the push token for silent push is updated,
70          // if necessary.
71
72          // Registering in this manner doesn’t mean that a user sees a notification. It only
73          // means that the application will receive a unique push token from iOS.
74          [[UIApplication sharedApplication] registerForRemoteNotifications];
75
76          // Request authorization from the user for push notification alerts.
77          [[UNUserNotificationCenter currentNotificationCenter]
78          requestAuthorizationWithOptions:UNAuthorizationOptionAlert |
79          UNAuthorizationOptionSound |
80          UNAuthorizationOptionBadge
81          completionHandler:^(BOOL granted, NSError *_Nullable error) {
82              if (error == nil) {
83                  if (granted == YES) {
84                      // Your application may want to do something specific if the user has
85                      // granted authorization for the notification types specified; it would be
86                      // done here.
87                      NSLog(@"User granted permission");
88                  }
89              }
90          }];
91      });
92  }
93
94  - (void)application:(UIApplication *)application
95      didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
96      [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) {
97          [mp setDeviceToken:deviceToken];
98      }];
99  }
100
101  - (void)application:(UIApplication *)application
102      didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
103      os_log_debug(OS_LOG_DEFAULT,
104        "didFailToRegisterForRemoteNotificationsWithError = %@", error);
105  }
106
107  // The method will be called on the delegate when the user responded to the notification by
108  // opening the application, dismissing the notification or choosing a UNNotificationAction.
109  // The delegate must be set before the application returns from
110  // applicationDidFinishLaunching:.
111  - (void)userNotificationCenter:(UNUserNotificationCenter *)center
112      didReceiveNotificationResponse:(UNNotificationResponse *)response
113      withCompletionHandler:(void (^)(void))completionHandler {
114      // tell the MarketingCloudSDK about the notification
115      [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) {
116          [mp setNotificationResponse:response];
117      }];
118      if (completionHandler != nil) {
119          completionHandler();
120      }
121  }
122
123  // The method will be called on the delegate only if the application is in the foreground.
124  // If the method is not implemented or the handler is not called in a timely manner then the
125  // notification will not be presented. The application can choose to have the notification
126  // presented as a sound, badge, alert and/or in the notification list. This decision should
127  // be based on whether the information in the notification is otherwise visible to the user.
128  - (void)userNotificationCenter:(UNUserNotificationCenter *)center
129      willPresentNotification:(UNNotification *)notification
130      withCompletionHandler:
131        (void (^)(UNNotificationPresentationOptions options))completionHandler {
132      completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert |
133        UNAuthorizationOptionBadge);
134  }
135
136  /** This delegate method offers an opportunity for applications with the "remote-notification"
137  background mode to fetch appropriate new data in response to an incoming remote notification.
138  You should call the fetchCompletionHandler as soon as you’re finished performing that
139  operation, so the system can accurately estimate its power and data cost.
140  This method will be invoked even if the application was launched or resumed because of the
141  remote notification. The respective delegate methods will be invoked first. Note that this
142  behavior is in contrast to application:didReceiveRemoteNotification:, which is not called in
143  those cases, and which will not be invoked if this method is implemented. **/
144  - (void)application:(UIApplication *)application
145      didReceiveRemoteNotification:(NSDictionary *)userInfo
146      fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
147      [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) {
148          [mp setNotificationUserInfo:userInfo];
149      }];
150      completionHandler(UIBackgroundFetchResultNewData);
151  }
152
153  @end

URL Handling 

The SDK doesn’t automatically present URLs from these sources.

  • CloudPages URLs from push notifications
  • OpenDirect URLs from push notifications
  • Action URLs from in-app messages

To handle URLs from push notifications, follow these steps.

  1. Set the setURLHandlingDelegate.
  2. Implement the URLHandlingDelegate.

Set the setURLHandlingDelegate 

To set the URLHandlingDelegate, update the AppDelegate as shown in this example.

View Swift code example
1func setupMobilePush() {
2      // Set the URLHandlingDelegate to a class adhering to the protocol.
3      // In this example, the AppDelegate class adheres to the protocol (see below)
4      // and handles URLs passed back from the SDK.
5      SFMCSdk.requestPushSdk { mp in
6          mp.setURLHandlingDelegate(self)
7      }
8
9      ...
10  }
View Objective-C code example
1// AppDelegate.h
2
3  #import <Flutter/Flutter.h>
4  #import <UIKit/UIKit.h>
5  #import <UserNotifications/UserNotifications.h>
6  // The SFMCSDK module is included in the dependency packages for the SDK. Don't install it
7  // separately or list it in your podfile.
8  #import <SFMCSDK/SFMCSDK.h>
9
10  //...
11
12  // Implement the SFMCSdkURLHandlingDelegate delegate
13  @interface AppDelegate : FlutterAppDelegate<UNUserNotificationCenterDelegate,
14    SFMCSdkURLHandlingDelegate>
15
16  @end
17
18
19  // AppDelegate.m
20
21  - (void)pushSetup {
22      // AppDelegate adheres to the SFMCSdkURLHandlingDelegate protocol
23      // and handles URLs passed back from the SDK in `sfmc_handleURL`.
24      [SFMCSdk requestPushSdk:^(id<PushInterface> _Nonnull mp) {
25          [mp setURLHandlingDelegate:self];
26      }];
27
28      //rest of pushSetup...
29  }

Implement the URLHandlingDelegate 

Implement the URLHandlingDelegate in AppDelegate, as shown in this example.

View Swift code example
1// AppDelegate.swift
2
3  //rest of AppDelegate.swift...
4
5  // REQUIRED IMPLEMENTATION
6  extension AppDelegate: URLHandlingDelegate {
7      /**
8       This method, if implemented, can be called when a Alert+CloudPage, Alert+OpenDirect,
9       Alert+Inbox or Inbox message is processed by the SDK.
10       Implementing this method allows the application to handle the URL from Marketing Cloud
11       Engagement data.
12
13       In versions of the SDK before version 6.0.0, the SDK automatically handled URLs and
14       presented them using a SFSafariViewController.
15
16       Because there are security risks inherent in URLs and web pages (such as Open Redirect
17       vulnerabilities), the app that implements the Engagement SDK is now responsible for
18       processing URLs. This reduces risk to the application by affording full control over
19       processing, presentation and security to the application code itself.
20
21       @param url value NSURL sent with the Location, CloudPage, OpenDirect or Inbox message
22       @param type value NSInteger enumeration of the source type of this URL
23       */
24      func sfmc_handleURL(_ url: URL, type: String) {
25          // Send the URL returned from the SDK to UIApplication to handle correctly.
26          UIApplication.shared.open(url,
27                                    options: [:],
28                                    completionHandler: {
29              (success) in
30              print("Open \(url): \(success)")
31          })
32      }
33  }
View Objective-C code example
1// AppDelegate.m
2
3  //rest of AppDelegate.m...
4
5  /**
6   This method, if implemented, can be called when a Alert+CloudPage, Alert+OpenDirect,
7   Alert+Inbox or Inbox message is processed by the SDK.
8   Implementing this method allows the application to handle the URL from Marketing Cloud
9   Engagement data.
10
11   Versions of the SDK before version 6.0.0 handled URLs automatically and presented them using
12   a SFSafariViewController.
13
14   Because there are security risks inherent in URLs and web pages (such as Open Redirect
15   vulnerabilities), the app that implements the SDK is now responsible for processing URLs.
16   This change reduces risk to the application by affording full control over processing,
17   presentation and security to the application code itself.
18
19   @param url value NSURL sent with the Location, CloudPage, OpenDirect or Inbox message
20   @param type value NSInteger enumeration of the source type of this URL
21   */
22  - (void)sfmc_handleURL:(NSURL * _Nonnull)url type:(NSString * _Nonnull)type {
23      if ([[UIApplication sharedApplication] canOpenURL:url]) {
24          [[UIApplication sharedApplication] openURL:url
25            options:@{}
26            completionHandler:^(BOOL success) {
27              if (success) {
28                  NSLog(@"url %@ opened successfully", url);
29              } else {
30                  NSLog(@"url %@ could not be opened", url);
31              }
32          }];
33      }
34  }
35
36  //rest of AppDelegate.m...

Enable Rich Notifications (Optional) 

Rich notifications include images, videos, titles, subtitles, and mutable content. Mutable content can include personalization in the title, subtitle, or body of your message.

For implementation details, see Send Rich Notifications.

Troubleshoot iOS Setup 

If you encounter a cycle error in your Flutter Xcode project after adding a Notification Service Extension, follow these steps to fix it.

  1. Navigate to YOUR_APP_TARGET in Xcode.
  2. With your app target selected, go to the Build Phases tab.
  3. Find Embed Foundation Extension.
  4. Drag and position it above both Thin Binary and Embed Pods Frameworks.

Reordering the build phases resolves the cycle error.

See Also