Latest Releases

Release notes

Getting Started with iOS

Requirements

  • Swift 5.3
  • Xcode 12.0 or above
  • Xcode Deployment Target iOS 11.0 or above
  • Your SDK Access Token (provided by Proxy)
  • Your App Key (Get one at https://docs.proxy.com/your-apps/)

Quick Start

A demo project Proxy ID Demo.xcodeproj is included with Proxy ID SDK for iOS. This project demonstrates how to authenticate a user to the Proxy service and enable the Proxy signal within your app.

Setup

Integrating the ProxyIDSDK.xcframework

Using SPM

Use the link https://px-id-sdk-ios:[SDK_ACCESS_TOKEN]@github.com/proxyco/ios-sdk-id to configure the ProxyIDSDK as an SPM dependency in Xcode. We advice to point directly to the version you want to integrate from releases

Manually

You can always just copy the ProxyIDSDK.xcframework bundle into your project and set it up as an Embed & Sign framework.

Configuring iOS privacy permissions

Proxy uses BLE(Bluetooth Low Energy) and requires these permissions for connecting to Proxy devices around you. These are the keys to be added for BLE to your app's Info plist file:

  • NSBluetoothAlwaysUsageDescription (String)
  • NSBluetoothPeripheralUsageDescription (String)

Proxy uses your location to set up geofences around places where you use Proxy, to automatically wake up the app at just the right time to interact with Proxy devices. We never track, store, or send your location anywhere. Proxy will still work without Location permissions, but it will not launch automatically after being terminated, requiring the user to launch the app before use. These are the keys to be added for Location to your app's Info plist file:

  • NSLocationWhenInUseUsageDescription (String)
  • NSLocationAlwaysAndWhenInUseUsageDescription (String)

Proxy might use the Camera to let the user set a profile picture:

  • NSCameraUsageDescription (String)

Example of privacy configuration in the app's Info plist file:

Example of privacy configuration

Configuring iOS background modes

The Proxy ID SDK communicates with Proxy devices and with the Proxy cloud service while your app is in the background, and requires the following background modes to operate correctly:

  • Location updates
  • Uses Bluetooth LE accessories
  • Acts as a Bluetooth LE accessory
  • Background fetch ( needed only for iOS < 13.0 )
  • Background processing
  • Remote notifications

Background processing ( iOS >= 13.0 ) and Background fetch ( iOS < 13.0 ) are used for access updates performed while your application is in the background. For "Background processing" to work, you must add the background task identifier co.proxy.ios.background.refresh under the heading "Permitted background task scheduler identifiers" in your Info plist file. In case your application supports iOS versions earlier than 13.0, for Background fetch to work, you must implement application(_:performFetchWithCompletionHandler:) in your app delegate and call refreshAccess:

ProxyID.shared.refreshAccess { _ in
    completionHandler(.newData)
}

Silent push notifications

Proxy uses silent push notifications to perform some required access refreshes e.g. when a user has been revoked access. In order for these push notifications to work, you have to:

/// register for remote notifications at app startup, for example application(_:didFinishLaunchingWithOptions:) method
/// of your app delegate
UIApplication.shared.registerForRemoteNotifications()

/// forward to Proxy the following remote notifications related events from your app delegate 
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    ProxyID.shared.applicationDidRegisterForRemoteNotificationsWithDeviceToken(deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    ProxyID.shared.applicationDidFailToRegisterForRemoteNotificationsWithError(error)
}

func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    if ProxyID.shared.canHandleNotification(userInfo: userInfo) {
        /// handle Proxy push notifications
        ProxyID.shared.handleNotification(userInfo: userInfo, fetchCompletionHandler: completionHandler)
    } else {
        // handle other types of push notifications supported by your application
    }
}

Basic usage of the SDK

Initializing

Proxy ID SDK exposes its APIs as methods under the ProxyID shared instance. Before being used, the SDK needs to be initialized in the method application(_:didFinishLaunchingWithOptions:) of your app delegate.

First, import the Proxy SDK at the top of the app delegate source file:

import ProxyIDSDK

Then, initialize the SDK in application(_:didFinishLaunchingWithOptions:), passing it a configuration with the following properties:

  • appKey: has as value the unique identifier you get from Proxy for your application. The app key can be obtained from here and is different from the SDK Access Token
  • environment: can be .staging or .production and represents the environment where Proxy will run; the default value for environment is .production
  • autoStart: communicates to the SDK if the Proxy Signal should also start as part of the initialization; the auto start will only happen if the Proxy Signal was ON at the time the application was last time closed; the default value for autoStart is true
/// Initializing the SDK with default values of .production for environment and true for autoStart
ProxyID.shared.initialize(config: ProxyConfig(appKey: "12345"))

/// Initializing the SDK with environment .staging and no autoStart
ProxyID.shared.initialize(config: ProxyConfig(appKey: "12345", environment: .staging, autoStart: false))

The SDK communicates different events of interest through a delegate. To register for any of those events, just provide a delegate that conforms to the protocol ProxyIDSDK.ProxyIDDelegate. There is no need to implement all the methods under this protocol, you can freely choose only those that are relevant for your application. Setting up the delegate is done like this:

ProxyID.shared.delegate = self

User authentication

The Proxy ID SDK includes standard user interface elements to make it simple to authenticate a Proxy user. At a minimum, you must provide the email address of the user you wish to authenticate with Proxy. This email address is used to verify the ownership of their Proxy account. You customize aspects of the authentication UI using an instance of ProxyAuthConfig that currently supports the following fields:

  • email - user's email address
  • appName - the name of your application, to be presented in the UI
  • appIcon - the icon of your application, to be presented in the UI
  • appColor - the accent color to use in the UI, but only if branded is true; default value for appColor is Proxy red
  • branded - if true, no Proxy branding elements except for a "Powered by Proxy" footer will be shown in the UI; default value for branded is false
  • requestBluetoothPermission - if true, the authentication flow includes a step for requesting bluetooth permission. Since Proxy cannot work without bluetooth, this step cannot be skipped unless the user grants bluetooth permission; default value for requestBluetoothPermission is true
  • requestLocationPermission - if true, the authentication flow includes a step for requesting location permission. Since Proxy can work without location permission, this step can be skipped by the user; default value for requestLocationPermission is true and you should make sure to set it to false in case you don't use proximity notifications (proximity notifications is the only SDK feature that relies on location)

Check if the user is already authenticated with ProxyID.isAuthenticated. Display the authentication UI only if ProxyID.isAuthenticated is false, as follows:

let config = ProxyAuthConfig(
    email: "test@test.com",
    appName: "My App",
    appIcon: UIImage(named: "my-icon")!)

    /// Shows UI for user to authenticate
    if !ProxyID.shared.isAuthenticated {
        ProxyID.shared.displayAuthentication(over: self, config: config) { user in
        /// authentication complete
        /// user of type ProxyPerson? contains the user details if the flow completed with the user
        /// being authenticated and is nil otherwise
        }
    }

Starting the Proxy Signal

Once the user is authenticated, your app can start emitting their Proxy Signal. This enables the user to use their Proxy Signal to interact with any Proxy supported device.

ProxyID.shared.start()

Implement the ProxyIDSDK.ProxyIDDelegate method proxyDidStart to perform any updates required once your app started emitting the Proxy Signal. Make sure bluetooth is ON and authorized before starting Proxy; as shown in the Demo app, you can check the value of ProxyID.state to achieve this.

Stopping the Proxy Signal

You can stop the Proxy service and stop emitting the user's Proxy Signal at any time. You may do this when the user has opted to disable Proxy functionality within your app:

ProxyID.shared.stop()

Implement the ProxyIDSDK.ProxyIDDelegate method proxyDidStop to perform any updates required once your app stopped emitting the Proxy Signal.

Checking if the Proxy signal is ON

You can check at any time if the SDK is emitting the user's Proxy Signal:

ProxyID.shared.isProxyOn ? print("ON") : print("OFF")

Signing out

You can instruct the Proxy SDK to forget its currently authenticated user and delete any cached data associated with their account. You should do this whenever the user signs out of your app to prevent leaking the user's data to the next user who signs in.

ProxyID.shared.signout()

Requesting access for Bluetooth and Location

Bluetooth

The SDK provides the convenience property isBluetoothAuthorized to detect if requesting bluetooth permission is needed. To request bluetooth permission, you can use the requestBluetoothPermission helper method:

if !ProxyID.shared.isBluetoothAuthorized {
    /// Updates on the bluetooth permission for this request are passed by the SDK to your application 
    /// through the proxyDidUpdate(bluetoothPermission:) delegate method.
    ProxyID.shared.requestBluetoothPermission()
}

If no action is taken by your application regarding bluetooth permission, the SDK will ask for this permission before starting to emit the Proxy Signal. As illustrated in the demo application, you should monitor changes to the ProxyID.state value to be able to react properly if bluetooth permission is revoked or bluetooth is turned OFF.

Location

The SDK provides the convenience property isLocationAuthorized to detect if requesting location permission is needed. To request location permission, you can use the requestLocationPermission helper method:

if !ProxyID.shared.isLocationAuthorized {
    ProxyID.shared.requestLocationPermission()
}

If no action is taken by your application regarding location permission, the SDK will ask for this permission as needed, before proximity notifications can be enabled. See the Proximity notifications sections for more details on this feature.

Proximity notifications

If enabled, proximity notifications will automatically wake up the app at just the right time to interact with Proxy devices. This happens based on geofences set up around places where you use Proxy. Proximity notifications are disabled by default. If you don't use this feature, location permissions are not needed at all by the SDK.

/// Use this method to enable proximity notifications
ProxyID.shared.enableProximityNotifications()

/// Use this method to disable proximity notifications
ProxyID.shared.disableProximityNotifications()

/// Querying the status of the proximity notifications feature
ProxyID.shared.isProximityNotificationsEnabled ? print("ON") : print("OFF")

Updates on the proximity notifications feature status are passed by the SDK to your application through the proxyProximityNotificationsDidUpdate(enabled:) delegate method.

In the SDK integration within the Proxy ID iOS application, proximity notifications are exposed to users under the name of Express Mode. These Express Mode details provide more insight on how proximity notifications work if enabled and how the necessary location permissions are requested in the Proxy ID app.

SDK additional capabilities

Custom UI Authentication

To verify the user, Proxy sends an email with a verification code to the user's email address. The user then enters the received verification code, which is passed back to the SDK to complete verification. This code is generally short-lived and must be used within soon after it is sent.

First, request a verification email to be sent to the user's email address:

ProxyID.shared.requestVerification(email: "test@test.com") { result in
    /// check result to verify if success or failure
}

Once the user has supplied you with the code they received in their inbox, your app can complete the user authentication with the SDK:

ProxyID.shared.authenticate(code: "1234", email: "test@test.com") { result in
    /// check result to verify if success or failure
}

Token-based Authentication

An additional way to verify the user is by using token-based authentication. A JWT must be generated via your own back-end using a client id with the same value as the app key used to integrate the SDK. This JWT can then be used to complete authentication with the SDK:

ProxyID.shared.authenticate(token: "xxxxx.yyyyy.zzzzz") { result in
    /// check result to verify if success or failure
}

The app key can be found at https://docs.proxy.com/your-apps/

Restarting the Proxy Signal

You can restart the Proxy Signal at any time, which translates into the Proxy service being stopped and started up again:

ProxyID.shared.restart()

Accessing user's access cards

Use the following method to get the access cards of the authenticated user:

ProxyID.shared.fetchCards { result in
    /// check result to verify if success or failure
    /// in case of success, result embeds an array of ProxyIDSDK.ProxyCard
}

Managing nearby devices

Use the devices property to access the current nearby devices, which is an array of ProxyIDSDK.ProxyDevice class instances:

print(ProxyID.shared.devices.map{ $0.title })

Updates on the nearby devices are passed by the SDK to your application through the following delegate methods:

  • proxyDidDiscover(device:) - a new device was discovered; if your app has its own list of devices, you should add the new device to this list - otherwise, just use the updated ProxyID.devices list
  • proxyDidBecomeLost(device:) - a device was lost; if your app has its own list of devices, you should remove the lost device from this list - otherwise, just use the updated ProxyID.devices list
  • proxyDidUpdate(device:) - a device was updated; if your app has its own list of devices, the device entry in that list has its fields already updated and you can proceed to refreshing the UI; please note that device updates are happening very often so you have to limit the UI refreshes triggered by them

To manually unlock a device, just call the unlock method on that device:

ProxyID.shared.devices.first?.unlock()

Troubleshooting

Console logs

For debugging purposes, the SDK can be configured to show Proxy logs in the console:

/// default value of this setting is false
ProxyID.shared.loggingEnabled = true

Sending feedback emails

The following method can be used to send feedback emails in case the need for troubleshooting arises:

ProxyID.shared.sendFeedback(presentingViewController: vc)

Feedback emails will contain important troubleshooting information like running environment details and gathered logs. The default recipients for the feedback mail composer are Proxy customer support email addresses and can be customised by passing your application specific customer support email addresses through the recipients parameter:

ProxyID.shared.sendFeedback(
    presentingViewController: vc, 
    recipients: ["recipient1@gmail.com", "recipient2@gmail.com"]
    )

Accessing the logs report

For an increased control over Proxy diagnostic logs, you can access the compressed logs report data directly and decide how to use it (e.g. uploading it to a well known location for further inspection):

ProxyID.shared.compressedLogsReport { data in
    // upload compressed report data
}

Access refresh

Some of the access issues that might occur while using Proxy can be caused by stale access data and might be solved by triggering an access refresh. This method should be called only as a response to a troubleshooting user action, calling it too often is highly discouraged.

ProxyID.shared.refreshAccess { result in
    /// check result to verify if success or failure
    /// in case of success, result embeds an array of ProxyIDSDK.ProxyCard
}

APNS setup

For silent push notifications to work, the pem formatted APNS certificate and key need to be added under your app at https://docs.proxy.com/your-apps. The APNS setup section for your app should look like this:

APNS setup section

Certificate setup

To transform a Keychain Access exported p12 certificate to a pem format, you can use the following command line instruction:

# apns-cert.p12 is the Keychain Access exported certificate file and apns-cert.pem is the output pem format file
openssl pkcs12 -clcerts -nokeys -out apns-cert.pem -in apns-cert.p12

The content of the pem file can then be copied into the APNS Certificate text field of your app APNS setup section.

# copy the certificate to clipboard with the command below and then 
# manually paste it into the APNS Certificate text field
pbcopy < apns-cert.pem

Key setup

To transform a Keychain Access exported p12 key to a pem format, you can use the following command line instructions:

# apns-key.p12 is the Keychain Access exported key file and apns-key.pem is the output pem format file
# this command will ask you for a PEM passphrase, so enter something of your choice e.g. 1111
openssl pkcs12 -nocerts -out apns-key.pem -in apns-key.p12

# the key in the apns-key.pem file generated in the previous step is encrypted, so this encryption has to be removed
# this command will ask you to enter the passphrase chosen at the previous step
openssl rsa -in apns-key.pem -out apns-key-noencryption.pem

The content of the non encrypted pem file can then be copied into the APNS Key text field of your app APNS setup section.

# copy the key to clipboard with the command below and then manually paste it into the APNS Key text field
pbcopy < apns-key-noencryption.pem