iOS Push Library
iOS Push Library
Overview
The iOS Push Library is intended to securely deliver ENGAGE® content from an iOS mobile application securely to an ENGAGE-enabled device. The Library provides the secure functionality to Push ENGAGE content by using an OAuth authentication scheme and setting the provided clientID and clientSecret in the pushlib-config.json file of the host app.
Library Version History
Version | Date | Changes |
---|---|---|
1.0 | 15-July-2020 | Initial release for ENGAGE iOS Client Push Library |
Pre-Integration Steps
First Orion will provide the following during pre-integration:
clientId/clientSecret – identity credentials used to retrieve a valid Authorization Token for securely pushing ENGAGE content
First Orion Artifactory Credentials – required for accessing the CocoaPods repository in which the Push Library is published, in the First Orion artifact repository
pushlib-config.json – configuration file for the host app to communicate with the ENGAGE platform
Integration Customer will provide First Orion:
Communication – notify the First Orion team as soon as possible if the ENGAGE-enabled host app will be an iOS application so First Orion may deliver all required configuration
Skills Required
- Apple Developer Portal
- Linux
- Swift
- Xcode
- CocoaPods
System Requirements
- MacOS
- Ruby >= 2.0
- CocoaPods >= 1.10
- CocoaPods Plugin to work with Artifactory >= 1.0.5
- Xcode >= 10
- Swift 4 or 5
Host App Requirements
- iOS 10.x or higher
- Ability to link Apple Dynamic Frameworks
- Swift (4 or 5) or Objective-C
- Provisioning Profiles
- Host App
Cross-Platform Compatability
Cross-platform mobile frameworks are not supported or compatible with ENGAGE SDK
See the Cross-Platform Framework section for more detail
Host App Impact
- SDK size: ~1mb
- Less if Bitcode-enabled
- Content push data usage: 100kb or less
Level of Effort
Task | Skillset | Time of Effort |
---|---|---|
Install CocoaPods | Linux | 10 min |
Install Artifactory plugin | Linux | 10 min |
Configure standard netric file to authenticate the First Orion Artifactory | Linux | 5 min |
Configure Podfile to resolve Push Library dependency | CocoaPods | 10 min |
Configure app requirements in Xcode and Apple Developer Portal | Xcode / Apple Developer Portal | 25 min |
Initialize and configure Push Library SDK in host app | Swift | 60 min |
Test Library integration steps: Update configuration file with credentials and Service Provider UUID Push ENGAGE content from device using Service Provider approved aNumber to ENGAGE-enabled bNumber Check Contact created on ENGAGE-enabed bNumber device Make phone call to device using aNumber that was pushed to ENGAGE-enabled bNumber device | 20 min |
Configure pushlib-config.json
The pushlib-config.json file defines the configuration settings that enable network communication with the ENGAGE platform. The only step required is to include this file in the main target of the host app Xcode project. First Orion will provide you with a configuration file before integration efforts begin.
Example pushlib-config.json
{
"test": {
"pushLibHost" : "api.fo-engage.com",
"clientId" : "2256njcldf6u6dh4r55gc06huz",
"clientSecret" :"97hieqqt5eip53ir92he9aa23ji1dhau9uts0do6pi4lbhqgoojd",
"serviceProviderUuid": "13f68f49-39e5-48de-9e4a-1b6e259e10a5",
"keychainAccessGroup": "X3RCY6K2JW.com.firstorion.engage.pushlib"
},
"production": {
"pushLibHost" : "api.fo-engage.com",
"clientId" : "2256njcldf6u6dh4r55gc06huz",
"clientSecret" :"97hieqqt5eip53ir92he9aa23ji1dhau9uts0do6pi4lbhqgoojd",
"serviceProviderUuid": "f5cfc9ca-5968-45a0-b2a5-69a0ebe4beb7",
"keychainAccessGroup": "X3RCY6K2JW.com.firstorion.engage.pushlib"
}
}
pushlib-config.json fields
The following fields are the required configuration parameters in the config file.
NOTE
There are two objects, test and production, that each contain all of these fields. These define the separate configurations for each environment of Push Library.
Field | Type | Description |
---|---|---|
pushLibHost | String | Base URL for communicating with ENGAGE platform |
clientId | String | Identity credentials used for retrieving Authorization Token |
clientSecret | String | Identity credentials used for retrieving Authorization Token |
serviceProviderUuid | String | Service Provider UUID associated with integration customer and business pushing ENGAGE content |
keychainAccessGroup | String | Adding the Keychain dependency requires sharing the host app Group ID with the Push Lib. This is required and can be found in Xcode under Signing & Capabilities of the host app |
Xcode Application Setup
The required Xcode configuration and impact is minimal. The most important step is ensuring that the host app has the Keychain Sharing capability properly configured.
Keychain Sharing
ENGAGE Platform requires a way to map requests made via PushLib to a particular device.
PushLib handles this by creating a unique identifier for the device, sending the identifier generated during the authentication request, before making other requests to the platform. This allows devices to leverage the library’s core functionality. The identifier needs to be unique and unchanging, even if the host app containing PushLib is deleted and reinstalled. For this reason, it is permanently stored in the Keychain.
Setting Up Keychain Sharing Capability
The Keychain Sharing capability must be enabled for PushLib to access the Keychain. Specifically, a Keychain Group must be added that will match the Bundle Identifier of the main target.
In Xcode, go to the project viewer that lists the targets of the app and choose the main target. Navigate to the Signing & Capabilities tab, then click on + Capability to add a capability. Search for Keychain Sharing in the window that appears and select it.
After selecting Keychain Sharing, it will appear as a new section in the Signing & Capabilities view. Under the list of Keychain Groups, which at this point should be empty, click the + button to add a new Keychain Group.
Xcode should automatically create a Keychain Group with the same name as the Bundle Identifier in the same target. You may copy-paste the Bundle Identifier into the Keychain Groups entry field.
Confirm the Keychain Group matches the Bundle Identifier:
Verifying PushLib Config File for Keychain Access
After the correct Keychain Group has been added, check the pushlib-config.json file’s keychainAccessGroup field (for each environment). The string value for this field should be a combination of the TeamId (not the Team name), which is set as the Team in the Xcode target, and the Keychain Group.
One way to find the TeamId is to check-in Apple Developer. Another way is to search the workspace for DEVELOPMENT_TEAM. This setting is found in the Build Settings tab in the project target view. The plain value, which is equivalent to the TeamId, is shown in the search result in the search bar on the left.
Once the TeamId value has been determined, verify that the keychainAccessGroup field is correct.
It is constructed as shown in this example:
Field | Example |
---|---|
Keychain Group | com.firstorion.engage.PushLibGoldCopy |
TeamID | UOMHJOJI6Y |
KeychainAccessGroup | UOMHJOJI6Y.com.firstorion.engage.PushLibGoldCopy |
Resolve Library Dependencies
The ENGAGE Push Library is published in the First Orion artifact repository. Credentials to access the repository will be provided by First Orion before integration efforts begin.
These artifact repository credentials:
- should be securely stored
- should never be checked into a source code repository
- should be appropriately retrieved during integration and app build processes
The suggested and preferred way to resolve the ENGAGE SDK dependencies is to access the binary from the First Orion Artifactory, which is a CocoaPod package. This particular method requires setting the correct system configurations to authenticate against Artifactory.
CocoaPod >=1.10 Requirement
The required CocoaPods version must be 1.10 or above to support the ENGAGE SDK which is compiled as an XCFramework.
Required Prerequisites
Ruby >= 2.0 installed
- CocoaPods >= 1.9 installed
- CocoaPods Artifactory Plugin >= 1.4 installed
Configure First Orion Artifactory
Install CocoaPods Artifactory Plugin
$ gem install cocoapods-art
Add First Orion Repository
To resolve ENGAGE pods, add First Orion repo to client
$ pod repo-art add cocoapods-local
"https://firstorion.jfrog.io/firstorion/api/pods/fo-cocoapods-external"
Authentication
To authenticate, please add the credentials to the client .netrc file
machine firstorion.jfrog.io
login <USERNAME>
password <PASSWORD>
Configure Podfile
Resolve Pods
Add First Orion repo to Podfile
plugin 'cocoapods-art', :sources => [
'fo-cocoapods-external'
]
Configure ENGAGE Pods
Add ENGAGE pod to Main Application target
platform :ios, '10.0'
use_frameworks!
plugin 'cocoapods-art', :sources => [
'cocoapods-local'
]
target 'PushLibGoldCopy' do
pod 'EngagePushLib', '= 1.0.0'
end
Install Pods
$ pod install
Troubleshooting CocoaPods
When working with CocoaPods, it is sometimes necessary to remove the local Pod repo and to re-sync. This may need to be done occasionally when new SDK releases become available and the client’s local CocoaPods repo needs to re-sync. The following steps outline the removal of the First Orion Pod repo from the client and re-syncing.
Remove Pods from Artifactory Repo
$ pod repo-art remove cocoapods-local
Remove Local CocoaPods References
$ rm -rf ~/.cocoapods/repos/cocoapods-local
Remove CocoaPod Cache, not always required
$ pod cache clean --all
Deintegrate Cocoapod, not always required
$ pod deintegrate
Re-add First Orion CocoaPods Repo
$ pod repo-art add cocoapods-local
"https://firstorion.jfrog.io/firstorion/api/pods/cocoapods-local"
Install Pods from Podfile
From the project’s main directory – pod install could also be used
$ pod update
Push Library Usage
Import PushLib
In any source file that uses PushLib, the line import EngagePushLib must be included in the import statements for that file.
Initialize PushLib
Before the iOS application can securely deliver ENGAGE content to an ENGAGE-enabled device, the Push Library must be configured by the app. This is best accomplished in the AppDelegate class:
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ... your app's other initialization logic
PushLib.shared.configurePushLib(forEnvironment: .test) {
optionalPushLibError in
PushLib.shared.configurePushLib(forEnvironment: .test) { optionalPushLibError
in
if let pushLibError = optionalPushLibError {
switch pushLibError {
case .notConfigured:
self.handleNotConfigured()
case .configurationFailure(let _, let webResponse):
self.handleConfigurationFailure(webResponse: webResponse)
case .deviceIdMissing:
self.handleDeviceIdMissing()
case .deviceIdGenerationFailure:
self.handleDeviceIdGenerationFailure()
case .deviceIdStorageFailure:
self.handleDeviceIdStorageFailure()
case .deviceIdRetrievalFailure:
self.handleDeviceIdRetrievalFailure()
default:
print("Unexpected PushLibError case encountered:
\(pushLibError.localizedDescription)")
}
} else {
print("Push Library configuration succeeded.")
}
if let pushLibError = optionalPushLibError {
print("Push Library configuration failed:
\(pushLibError.localizedDescription)")
} else {
print("Push Library configuration succeeded.")
}
}
The usage of Push Lib’s configurePushLib function is as follows:
Parameter | Type | Description |
---|---|---|
forEnvironment | PushLibEnvironment (See Following Table) | Used to determine with which ENGAGE environment to interact, pulling configuration from the pushlib-config.json file. Possible values are: .test and .production |
completion | Closure (optional) | Takes a single argument, an optional PushLibError. It is nil if no error occurred |
The configurePushLib function reads pushlib-config.json, using the values for the environment specified as the forEnvironment parameter, to set up the PushLib. It also requests an authorization token from ENGAGE to use in later network requests. Once completed, and if optional parameter has been selected, it calls completion.
The following PushLibError cases may occur within this function:
notConfigured
configurationFailure
deviceIdMissing
deviceIdGenerationFailure
deviceIdStorageFailure
deviceIdRetrievalFailure
PushLibEnvironment
The environment in which to use the Push Library. An enum type.
Enum Value | Description |
---|---|
test | Test environment. Should be used with any non-production environment by the host app |
production | Test environment. Should be used with any non-production environment by the host app |
PushLibError
PushLibError is a custom Swift Error type and is therefore an enum type.
Enum Value | Description |
---|---|
notConfigured | Push Library is not configured Host app needs to ensure it is calling configurePushLib, probably from AppDelegate’s didFinishLaunchingWithOptions. Could be caused by configurationFailure |
configurationFailure | Push Library configuration failed Contains network property, which is an optional PushLibWebResponse (See following table) and will be present if configuration failed due to an API or network error. Host app needs to verify that it contains a pushlib-config.json file that is formatted correctly and contains appropriate values |
deviceIdMissing | There is no device ID stored in the device’s keychain Host app needs to ensure it is calling configurePushLib Device ID is also managed in this function. Could be caused by: deviceIdGenerationFailure or deviceIdStorageFailure |
deviceIdGenerationFailure | A device ID could not be generated for this device Host app might not be able to recover from this error. Currently, Device ID is being set to identifierForVendor Check: https://developer.apple.com/documentation/uikit/uidevice/1620059 -identifierforvendor If it is absent, device ID cannot be set |
deviceIdStorageFailure | A device ID could not be stored on this device’s keychain Host app needs to verify that it includes in its entitlements the permission to access the keychain access group equal to the keychainAccessGroup value in pushlib-config.json. This error might also be caused by other types of failures when attempting to store data to the keychain |
deviceIdRetrievalFailure | The device ID could not be retrieved from the keychain The same action should be taken for the host app as in deviceIdStorageFailure |
contentProviderError | The content providers could not be retrieved Contains network property, which is an optional PushLibWebResponse (See following table) and will be present if fetching the content providers failed due to an API or network error. Host app should ensure that the serviceProviderUuid property in its pushlib-config.json file is valid |
aNumberFormatError | The aNumbers property, defined in the PushProperties (See following table) object, was invalid The host app needs to ensure that each string value in the aNumbers array is formatted as an E.164 phone number |
bNumberFormatError | The bNumber property, defined in the PushProperties (See following table) object, was invalid The host app needs to ensure that the bNumber string value is formatted as an E.164 phone number |
sendPushNetworkError | The content push could not be sent due to an API or network error Contains network property, which is an optional PushLibWebResponse (See following table) and will be present if the API provided a response to the request. Host app should ensure all parameters used in the call to sendPush are valid |
sendPushPlatformError | The content push was attempted but could not be sent due to an external condition (e.g., the bNumber is from a device that is not ENGAGE-enabled) Contains platform property, which is an optional PushLibPlatformResponse (See following table) and will be present if the API provided a response to the request. Host app should ensure it is calling sendPush with appropriate parameter values (e.g., the bNumber should be from an ENGAGE-enabled device, maxWaitSeconds should be enough to ensure the push can be sent) |
PushLibWebResponse
Represents the API response received from a request that did not succeed.
Field | Type | Description |
---|---|---|
httpStatusCode | Integer | HTTP status code of the response |
responseBody | String (optional) | Body (JSON, if present) of the response |
PushLibPlatformResponse
Represents the API response received from a request to send a content push that was attempted and failed to send the push.
Field | Type | Description |
---|---|---|
response | String (optional) | A shorthand representation of the failure |
message | String (optional) | An explanation of the failure |
errorCode | Integer (optional) | Represents the type of error that occurred |
requestId | String (optional) | An identifier for the failed request to send push |
Retrieve Content Provider List
When the iOS app needs to get the list of accessible content providers, it may call the
getContentProviders function and used as shown:
PushLib.shared.getContentProviders { result in
switch(result) {
case .success(let contentProviders):
print("Successfully retrieved \(contentProviders.count) content
provider(s).")
case .failure(let pushLibError):
switch pushLibError {
case .notConfigured:
self.handleNotConfigured()
case .configurationFailure(let _, let webResponse):
self.handleConfigurationFailure(webResponse: webResponse)
case .deviceIdMissing:
self.handleDeviceIdMissing()
case .deviceIdRetrievalFailure:
self.handleDeviceIdRetrievalFailure()
case .contentProviderError(let _, let webResponse):
self.handleContentProviderError(webResponse: webResponse)
default:
print("Unexpected PushLibError case encountered:
\(pushLibError.localizedDescription)")
}
}
}
Parameter | Type | Description |
---|---|---|
completion | Closure | When getContentProviders is done, it calls this closure, which takes one argument of the type: Result<[ContentProvider],PushLibError> This Result object may be unwrapped to get: ContentProviders, if the function succeeded Error, if it failed |
The following PushLibError cases may occur within this function:
notConfigured
configurationFailure
deviceIdMissing
deviceIdRetrievalFailure
contentProviderError
ContentProvider
A content provider, which can be retrieved from the getContentProviders function.
Field | Type | Description |
---|---|---|
content | Content (See following table) | Regular content of the content provider |
preferredContent | Content | Preferred content of the content provider |
isPreferredContent | Boolean | Whether this content provider uses preferred content |
Content
Content of a content provider.
Field | Type | Description |
---|---|---|
bane | String | Name of the preferred content program |
uuid | String | Unique value representing preferred content program created in the portal. If this value is returned as all zeros, the entry is not considered preferred content |
Push Content
When the iOS app needs to push content, it can call the sendPush function as shown:
do {
let pushProperties = try PushProperties(aNumbers: [”+15016543210",
”+15011234567”], bNumber: "+15013334444")
PushLib.shared.sendPush(withProperties: pushProperties,
programContentUuid: ”521863c3-2f10-37a2-7bcd-b268012f09a4",
contentProviderUuid: ) { optionalPushLibError in
if let pushLibError = optionalPushLibError {
// handle any error from PushLib.shared.sendPush
switch pushLibError {
case .notConfigured
self.handleNotConfigured()
case .configurationFailure(let _, let webResponse)
self.handleConfigurationFailure(webResponse: webResponse)
case .deviceIdMissing
self.handleDeviceldMissing()
case .deviceldRetrievalFailure
self.handleDeviceldRetrievalFailure()
case .sendPushNetworkError(let _, let webResponse)
self.handleSendPushNetworkError(webResponse: webResponse)
case .sendPushPlatformError(let _, let platformResponse)
self.handleSendPushPlatformError(platformResponse:
platformResponse)
default:
print("Unexpected PushLibError case encountered:
\ (pushLibError.localizedDescription)")
}
} else {
print("Pushing Content succeeded.”)
}
}
} catch {
// handle any error thrown by PushProperties.init
guard let pushLibError = error as? PushLibError else {
print("Unexpected error encountered: \(error)")
return
}
switch pushLibError {
case .aNumberFormatError
handleANumberFormatError()
case .bNumberFormatError
handleBNumberFormatError()
default:
print("Unexpected PushLibError case encountered:
\ (pushLibError.localizedDescription)")
}
}
PushProperties
Properties that define a push.
See Glossary for further definition of terms
Push Property | Type | Required | Description |
---|---|---|---|
bNumber | String | Yes | Device number that will display program content when call is received after a successful push Must be E.164 format |
customData | Dictionary ([String:String]) | No | Custom text sent to bNumber and displayed during call. Recommended value: 20 First Orion best practice: maximum of 20 characters as messages over 20 characters may not display correctly on all devices |
timeToLiveSeconds | Integer | No | Duration, in seconds, for content to live on the device following a successful content push Default is 3600 Recommended value: 960 seconds First Orion best practice: set the TTL to 16 minutes |
keepAfterCall | Boolean | No | Set if content should be kept on device after call Default is false Recommended value: false First Orion best practice: do not keep content on the device after a call or successful push |
maxWaitSecond | Integer | No | Max wait time, in seconds, for response from the device, to the handset, to pushing content Recommended value: 10 seconds |
aNumbers | [String] | Yes | Originating numbers calling a bNumber. aNumber must be previously uploaded in the Branded Communication Content Portal List of up to 5,000 aNumbers in E.164 format Cannot send UUID |
Updated about 1 year ago