Tag Archives: iOS

Adding iOS App Extensions to Salesforce Mobile SDK Apps

If you’re an iOS developer who wants to use cool iOS app extensions in your Salesforce Mobile SDK app–this post’s for you.

iOS app extensions provide opportunities for developers to present their app’s functionality outside of their app context. With Mobile SDK, for example, you can use an extension to show a “most recently used” (MRU) list of accounts in the Today view and also when the user applies 3D Touch to your app icon. The realm of possibilities for extension functionality is vast. If you’re not familiar with iOS app extensions, get started at https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html (or search for “iOS app extensions” at https://developer.apple.com/library).

This post gives you some pointers for combining iOS app extensions with Mobile SDK 5.0 native apps. The instructions here are generic enough to be used with all types of extensions. To study a functioning example of an iOS app extension working in a Mobile SDK app, see the SmartSyncExplorer sample app at https://github.com/forcedotcom/SalesforceMobileSDK-iOS/tree/master/native/SampleApps/SmartSyncExplorer. The SmartSyncExplorer app demonstrates 3D Touch and Today view extensions. It also showcases the use of SmartStore in the extension’s widgetPerformUpdateWithCompletionHandler: method.

Let’s get going with configuring your Xcode workspace and customizing your app.

Configure Your Workspace

When you create an iOS app extension in Xcode, you add another build target to your app. Extensions require your main app target and the extension app target to join the same app group. You configure this relationship in your app’s Xcode workspace configuration.

The following steps don’t directly provide instructions for creating an extension–how you handle step 1 is between you and iOS. The subsequent steps describe the Xcode workspace changes that Mobile SDK and iOS require.

  1. In your Mobile SDK app, create an extension target as described in the Apple developer documentation.
  2. Select the top-level node of your Mobile SDK project in the Xcode Project Navigator. This step opens your app’s configuration wizard.
  3. Click General, and then specify a unique bundle identifier for the Mobile SDK app target. This screen capture shows where to set this identifier for a Mobile SDK app target.Bundle setting for iOS app extensons
  4. Repeat the bundle identifier step for the extension target. This identifier must also be unique.
  5. Enable App Groups and Keychain Sharing in your Mobile SDK target.
    1. In your app configuration, choose your Mobile SDK app target and click Capabilities.
    2. Under App Groups, select or create an app group. Use a unique label that identifies your app, such as “group.myapp.shared”.
    3. Under Keychain Sharing, select or create a keychain group. Use a unique label that identifies your app, such as “com.myapp.MyApp”. App group and keychain sharing settings for Mobile SDK app using iOS app extensions
  6. Repeat the App Groups and Keychain Sharing steps for your extension target. NOTE : The two values in the Extension target must exactly match the corresponding values in the application target.App group and keychain sharing settings for iOS app extension

Customize Your Code

To support extensions, you change your AppDelegate class to make it aware of the extension, and then you add similar code to your app extension view controller.

By default, the Mobile SDK AppDelegate class contains “bootstrapping” code that initializes the SDK. When you incorporate an iOS app extension, you add a couple of lines that tell the SDK that you’re working in an app group.

AppDelegate Code Changes

The following steps apply to the init method of your main app’s AppDelegate class.

  • In the init method, add calls to appGroupName and appGroupEnabled methods of SFSDKDatasharingHelper before the existing calls to SalesforceSDKManager.
// Insert these two lines, using your app group name
[SFSDKDatasharingHelper sharedInstance].appGroupName = 
    @"<your app group name>"; 
[SFSDKDatasharingHelper sharedInstance].appGroupEnabled = YES;

// Now it's OK to set SalesforceSDKManager properties ...
[SalesforceSDKManager sharedManager].connectedAppId = 
    @"<your consumer key>"
[SalesforceSDKManager sharedManager].connectedAppCallbackUri = 
    @"<your callback URL>";
[SalesforceSDKManager sharedManager].authScopes = 
    @[@"api", @"web", ...];
  • In the postLaunchAction block, use NSUserDefaults to cache a flag that indicates login success.
[SalesforceSDKManager sharedManager].postLaunchAction = 
    ^(SFSDKLaunchAction launchActionList) {
    ...
    /* Write a boolean indicating that user has logged in 
      using the app. You could share data between your app 
      and extension by saving it into your app group 
      using NSUserDefaults */
    [[NSUserDefaults initWithSuiteName:@"<your app group name>"] 
        setBool:@YES forKey:@"userLoggedIn"];
}

App Extension Code Changes

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 AppDelegate class. You also set the following SalesforceSDKManager properties in your extension view controller just as you do in your AppDelegate class:

[SalesforceSDKManager sharedManager].connectedAppId
[SalesforceSDKManager sharedManager].connectedAppCallbackUri
[SalesforceSDKManager sharedManager].authScopes

However, extensions can’t perform authentication tasks such as user logins. Instead, you check the NSUserDefaults value that you stored in the AppDelegate code to verify that a user has logged in before calling SalesforceSDKManager. The rest of your extension code is up to you.

The following steps walk you through the required customizations.

  • In your iOS app extension’s view controller class, find your extension’s initialization entry point and add the following Mobile SDK bootstrapping code there.
[SFSDKDatasharingHelper sharedInstance].appGroupName = 
    @"<your app group name>";
[SFSDKDatasharingHelper sharedInstance].appGroupEnabled = YES;

/* Before calling SalesforceSDKManager, check whether a user has 
logged in through the main app. As your condition, use the 
userLoggedIn Boolean value that you set in your app's 
postLaunchAction block. Remember that you saved this value in 
your app group using NSUserDefaults. */

if ([[NSUserDefaults initWithSuiteName:@"<your app group name>"] 
    boolForKey:@"userLoggedIn"]]) {
       
    // Now you can set the following SalesforceSDKManager properties 
    // as you did in your AppDelegate init method
    [SalesforceSDKManager sharedManager].connectedAppId = 
        @"<your consumer key>"
    [SalesforceSDKManager sharedManager].connectedAppCallbackUri = 
        @"<your callback URL>";
    [SalesforceSDKManager sharedManager].authScopes = 
        @[@"api", @"web", ...];
    // Call other Mobile SDK APIs
    // ...
}
// Continue with standard extension implementation
...
  • If the bootstrapping succeeds, your app can use the current user’s shared credentials to access Salesforce data directly in the extension. The following example shows typical REST API calls that you might add to an extension.
...
NSDictionary *fields = @{@"FirstName": @"\%",@"LastName": @"\%"};
SFRestRequest* request = [[SFRestAPI sharedInstance]
    requestForQuery:@"SELECT FirstName,LastName FROM Contact 
        ORDER BY CreatedDate DESC LIMIT 5"];
[[SFRestAPI sharedInstance] send:request delegate:self];
...

REMEMBER: 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.

For testing iOS app extensions, there’s one important restriction: You must use a real device. You can’t test iOS app extensions in an iOS simulator.

Conclusion

At this point, you’ve learned how to configure a Mobile SDK app to use an iOS app extension. Your next step should be to implement your own extension in a Mobile SDK app. The type of extension you implement is up to you. If you repurpose the code in the SmartSyncExplorer sample app, you can quickly get running with 3D press and Today screen extensions. By default, both widgets show a list of MRU contacts.

The iOS app extensions feature offers a plethora of other possibilities. We’d love to see what you come up with!

Resources

The following resources provide general information on using Mobile SDK for iOS.

Salesforce Mobile SDK Development Guide:
https://developer.salesforce.com/docs/atlas.en-us.mobile_sdk.meta/mobile_sdk/ios_intro.htm

Trailhead module:
https://trailhead.salesforce.com/modules/mobile_sdk_native_ios

Advertisements

Mobile SDK 4.0.2 Is Now Available

 

Following close on the heels of our 4.0 major release and 4.0.1 patch, the 4.0.2 patch for Mobile SDK (Android and iOS) is now available. This patch primarily affects CocoaPods podspecs for iOS developers. We’ve also republished both forceios and forcedroid to keep them on the same version. See the section below for more details on what’s new.

To upgrade:

  1. Uninstall and reinstall forceios and forcedroid from npm.
  2. On iOS, rework your podspecs as described in the following section.
  3. If you use the GitHub repos, merge these tags:
    Android: https://github.com/forcedotcom/SalesforceMobileSDK-Android/tree/v4.0.2
    iOS: https://github.com/forcedotcom/SalesforceMobileSDK-iOS/tree/v4.0.2

CocoaPods Reorganization

In Mobile SDK for iOS 4.0.1, our podspec configuration didn’t exert enough control over external dependency versions. For 4.0.2, we’ve now removed the SalesforceMobileSDK-iOS podspec and replaced it with a separate podspec for each Mobile SDK module. We’ve also added a new GitHub repo, https://github/forcedotcom/SalesforceMobileSDK-iOS-Specs, that serves as the archive for all versions of our podspecs.

The new podspecs are:

SalesforceSDKCore—Implements OAuth and passcodes. All other pods except FMDB depend on this pod, either directly or indirectly.
SalesforceNetwork—Networking library (with transparent token refresh when session expires). Depends on SalesforceSDKCore.
SalesforceRestAPI—REST API wrappers for accessing Salesforce data. Depends on SalesforceNetwork.
FMDB—The Mobile SDK fork of FMDB, a third-party Objective-C wrapper for SQLite. This fork implements customized logging and
imports SQLCipher with full-text search support.
SmartStore—Implements secure offline storage. Depends on FMDB and SalesforceSDKCore.
SmartSync—Implements offline synchronization. Depends on SalesforceRestAPI and SmartStore.
SalesforceReact—Implements Salesforce Mobile SDK React Native bridges for apps written with React JavaScript and markup. Depends on SmartSync.

Here’s how to update your Podfile for the 4.0.2 patch.

1. In your project’s Podfile, add the SalesforceMobileSDK-iOS-Specs repo as a source before adding the CocoaPods source.

target 'YourAppName' do
# must get SalesforceMobileSDK-iOS-Specs 
# source first (before CocoaPods)! 
source 'https://github.com/forcedotcom/SalesforceMobileSDK-iOS-Specs.git' 
source 'https://github.com/CocoaPods/Specs.git'

2. Declare each pod on a separate line.

pod 'SalesforceSDKCore'
pod 'SalesforceNetwork'
pod 'SalesforceRestAPI'

Or, thanks to dependency chains, this will work if you’re using the current release:

pod 'SalesforceRestAPI'

If you currently have a podspec that calls pod SalesforceMobileSDK-iOS with subspecs for Mobile SDK modules, such as:

pod 'SalesforceMobileSDK-iOS', :subspecs =>[ 'SalesforceRestAPI' ]

Replace this call with individual pod calls, as shown above.

Working with an Unstable Branch

To work with an upcoming release of Mobile SDK, you clone the unstable branch of the SalesforceMobileSDK-iOS repo, and then pull resources from it.

  1. Clone github.com/forcedotcom/SalesforceMobileSDK-iOS locally at the desired commit.
  2. At the terminal window, run ./install.sh in the root directory of your clone.
  3. To each pod call in your Podfile, add a :path parameter that points to your clone. Note that you shouldn’t skip dependencies if you’re specifying a path—you need to call the pod command individually for each module.

Here’s the previous example repurposed to pull resources from a local clone:

target 'YourAppName' do
# must get SalesforceMobileSDK-iOS-Specs 
# source first (before CocoaPods)! 
source 'https://github.com/forcedotcom/SalesforceMobileSDK-iOS-Specs.git' 
source 'https://github.com/CocoaPods/Specs.git' 

pod 'SalesforceSDKCore', :path => 
    '/<path-to-clone-of>/SalesforceMobileSDK-iOS/' 
pod 'SalesforceNetwork', :path => 
    '/<path-to-clone-of>/SalesforceMobileSDK-iOS/' 
pod 'SalesforceRestAPI', :path => 
    '/<path-to-clone-of>/SalesforceMobileSDK-iOS/' 

end

For more information, see the Salesforce Mobile SDK Development Guide at https://github.com/forcedotcom/SalesforceMobileSDK-Shared/blob/master/doc/mobile_sdk.pdf?raw=true.