At Deloitte, we work with several software platform vendors to provide solutions for our clients. Building on top of platforms that provide many features out of the box allows us to focus more on specific client needs and less on technical boilerplate. One of the vendors we work with often is Salesforce, who offer an extensive cloud platform.
To make these platforms accessible on mobile we develop apps that integrate with different backend systems. When using Salesforce as a backend, the company provides a Mobile SDK. It provides tight integration with their platform while still having the full flexibility of native development. It comes in multiple flavors (iOS, Android, React Native & Cordova) and contains features such as authentication, offline data storage and data synchronization.
The Mobile SDK is very helpful when the rest of the app’s architecture fits with how it works, i.e. direct synchronization of SObjects via Mobile Sync. However, it also comes with some downsides when working on a React Native app:
- The entire SDK is compiled into the app, which often means several aspects are not used. Unused code inflates app size which may result in less downloads or more uninstalls.
- Non-standard way of integrating with React Native. Invasive changes in the apps’ native code (AppDelegate on iOS, MainActivity/MainApplication on Android) make it harder to integrate other native dependencies and upgrade between React Native versions.
- Dependence on CocoaPods
use_frameworks
! which is incompatible with many other (3rd party) native dependencies. Results in install failures and requires post-install workarounds to resolve. - Pinning of specific React Native versions, blocking upgrades until the Mobile SDK is upgraded.
- Not following best practices with OAuth login process (RFC 8252) and few options to customize (title color on iOS) the login screen. Our customers and their users expect a fully branded experience.
- Limited options to customize the login screen on iOS, no customizations options available on Android.
Not all apps require all functionality the Mobile SDK provides. Authentication and performing API requests may be enough. If you decide that it is not right for your project, there’s an alternative way of working with Salesforce.
Authenticating with Salesforce
Authenticating with Salesforce is done via OAuth. There’s a couple of libraries such as react-native-app-auth or expo-auth-session that help using OAuth in React Native. Either of these libraries is easily integrated with any React Native project, avoiding many of the code changes required by the Salesforce SDK and difficulties with CocoaPods.
Obtaining the proper configuration is done via the following this guide from Salesforce with the note that the “Consumer Key” in Salesforce is the client ID used by OAuth. Note that “Require Secret for Web Server Flow” must be disabled (usage of client secrets also isn’t recommended for mobile clients according to the guidelines). Once authentication is complete and the chosen OAuth library returns with the users’ access and refresh token, these can be persisted using libraries such as react-native-keygen or expo-secure-store in the devices’ encrypted storage.
Using these credentials, it’s time to send authenticated requests to your Salesforce instance. Handling HTTPS requests is easily done via built-in fetch with the appropriate headers set, example:
const accessToken = await SecureStore.getItemAsync("accessToken");
const instanceUrl = "https://instance.my.salesforce.com";
const fetchUrl = instanceUrl + "/services/apexrest/...";
const method = "GET";
const response = await fetch(fetchUrl, {
method,
body: body !== undefined && method !== "GET" ? JSON.stringify(body) : undefined,
headers: {
Authorization: "Bearer " + accessToken,
Host: instanceUrl.substring(8).toLowerCase(),
"Content-Type": "application/json"
},
});
Refreshing the tokens
All good things come to an end, and so do user sessions. After some amount of time, the access token will become invalid. This is signaled if making a regular request (see previous example) returns a HTTP status code 401. The refresh token obtained during authentication can then be used to obtain a fresh access token. In the process, the refresh token itself may also be refreshed according to the specification, so you must be ready for it. The newly obtained credentials must then be used for all subsequent requests. Example code:
const { accessToken, refreshToken } = await refresh(
{ ...authConfig, scopes: [] }, // authConfig is config used when authenticatin
{ refreshToken: currentRefreshToken }
);
// access token is a fresh token
// refresh token is a fresh token OR null OR empty string
And that’s all that is needed to perform requests with a Salesforce back-end! Usage of community-standard libraries and standard JavaScript gives full power over the process and is easier from a complexity and maintenance point of view.
Connecting these pieces, the next step is to write a communication layer that handles all requests to Salesforce. It handles authentication automatically by setting the appropriate headers. When it detects that the token needs to be refreshed (through a HTTP 401 response), it will attempt to refresh the sessions and retry the original request transparently.
Conclusion
When building a React Native app using a Salesforce back-end, think about the integration architecture and offline capabilities the app should have. If back-end communication is (mostly) done by syncing SObjects to and from the server, using the Salesforce Mobile SDK with MobileSync is a great approach. It will also give you offline support out of the box.
But, if the back-end exposes custom endpoints and/or offline capabilities are not required, a custom integration will be easier to maintain and imposes less restrictions on the standard React Native template.