Using iOS App Extensions with Mobile SDK
iOS app extensions provide opportunities for developers to extend their app’s functionality beyond the app window. Mobile SDK supports app extensions with only a small amount of extra configuration.
An iOS app extension is a separate target in your project. It lives in its own folder and is added to your app bundle as a separate binary unit. However, the app extension can access the same resources and libraries as your main app. Apple offers many extension types, as described in App Extensions articles at developer.apple.com.
To enable extensions, you add special configuration in two areas:
- Project settings: When you add an app extension, Xcode creates an app extension build target. You configure the existing main target and the new extension target to join the same app group and keychain sharing group. Both apps must sign in to the same provisioning profile.
- Application code: At runtime, the two apps must use identical app group information, and the main app must convey user authentication status to the extension.
After everything is properly configured, your app extension can run any Mobile SDK code that’s appropriate for the extension type.
You can reliably test app extensions only on physical devices. Simulator tests can give unpredictable results.
- In your Mobile SDK app, create an extension target as described in the Apple developer documentation. How you handle this step and the type of extension is between you and iOS.
- After you’ve created the target, select the top-level node of your Mobile SDK workspace in the Xcode Project Navigator. This step opens your project in the Project Editor.
- In both app and extension targets, sign in to the same developer team.
- Click General, and then specify a unique bundle identifier for the app target. Here’s how it looks in Xcode 12.4:
- Repeat the bundle identifier step for the extension target. This identifier must also be unique.
- In your app configuration, select your Mobile SDK app target and then click Capabilities.
- Turn on App Groups and Keychain Sharing in your app target.
- Under App Groups, select or create an app group. Use a unique label that identifies your app, such as “group.myapp.shared”.
- Under Keychain Sharing, select or create a keychain group. Use a unique label that identifies your app, such as “com.myapp.MyApp”.
- Repeat the App Groups and Keychain Sharing steps for your extension target. The two values in the extension target must exactly match the corresponding values in the application target.
When you incorporate an iOS app extension into a forceios app, you add code that tells Mobile SDK that you’re working in an app group. You add these lines in two places: the AppDelegate
class in your main app, and the view controller or scene delegate class in your app extension.
Mobile SDK provides a utility class, SFSDKDatasharingHelper
, that stores the necessary properties. Values you set for these properties in the main app and the app extension must be identical.
After the user attempts to log in to Salesforce, you store the authentication status where the extension code can access it. To do so, use an NSUserDefaults
dictionary whose suite name matches your app group name. Mobile SDK provides a NSUserDefaults (SFAdditions)
category that provides such a dictionary. This category defines one class method, msdkUserDefaults
, that returns a group dictionary if the appGroupEnabled
flag of the singleton SFSDKDatasharingHelper
object is set to YES. Here’s the SDK category:
The following steps apply to the init
method of your main app’s AppDelegate
class.
-
At the top of the
init
method, setappGroupName
andappGroupEnabled
on theSFSDKDatasharingHelper
shared instance. -
After the user has successfully logged in, store a value reflecting the login status in an object that the extension code can access. You can infer the status by reading the
currentUser
property of theSFUserAccountManager
object. To safely and consistently update the login status, set this property in the callback method of theregisterBlockForCurrentUserChangeNotifications
call in theinit
method. The following example uses the Mobile SDKNSUserDefaults (SFAdditions)
category to store the Boolean value under a key nameduserLoggedIn
.
At runtime, your iOS app extension operates as a second app, so you have to “bootstrap” it as well. You apply the same appGroupName
and appGroupEnabled
changes as you did in the main app’s AppDelegate
class.
App extensions can’t perform authentication tasks such as user logins. However, before making calls to a Mobile SDK manager such as MobileSyncSDKManager
, you must verify that a user has logged in. You do this verification by checking the value stored by the main app in the NSUserDefaults
dictionary.
-
In your app extension’s initialization entry point, set and enable the app group.
-
Verify that the
userLoggedIn
value indicates success. Continue with other Mobile SDK calls only ifuserLoggedIn
equals YES.
If the app determines that a user has successfully logged in, the app extension can apply that user’s shared credentials to Salesforce API calls.
- It’s the developer’s responsibility to determine the user’s login status. The iOS app extension code must not attempt to invoke the
SalesforceSDKManager
object before the user successfully logs in.
The following code is taken from the MobileSyncExplorer native sample app. This sample defines an app extension that displays a list of MRU Contact records.
Besides the app and extension targets, the MobileSyncExplorer project defines a third target, MobileSyncExplorerCommon
. This container module is accessible in main and extension targets but isn’t part of the app group. It implements a MobileSyncExplorerConfig
class that declares and manages three properties:
- appGroupName
The “app group” name, as defined in the project settings. This string is also hard-coded in the
MobileSyncExplorerConfig
class.- appGroupsEnabled
Boolean that indicates whether app groups are enabled.
- userLogInStatusKey
The name of the key used to store the user login status in the group
NSUserDefaults
dictionary.
At runtime, both the main app and the extension use the values appGroupName
and appGroupsEnabled
properties to set matching properties in their own copies of the SFSDKDataSharingHelper
class. When properly configured, this class enables both apps to use the login credentials of the current user to make calls to the Salesforce API.
To retain the user login status, MobileSyncExplorer defines a private method named resetUserloginStatus
. This method stores the given value in the user defaults dictionary under the userLoggedIn
key.
The init
method uses a call to resetUserloginStatus
to simplify the registerBlockForCurrentUserChangeNotifications
block.
Notice that this code uses an instance of MobileSyncExplorerConfig
to store the appGroupName
and appGroupsEnabled
properties.
The app extension is implemented in the RecentContactsTodayExtension/TodayViewController.m
file. For a Today extension, the entry point to custom code is widgetPerformUpdateWithCompletionHandler:
. The app extension reads the userLogInStatusKey
value, which it queries through its custom userIsLoggedIn
“getter” method. Notice that this getter method retrieves the app group information where the main app stored them—in the shared MobileSyncExplorerConfig
object.