Arrive iOS SDK

Introduction

The Arrive iOS SDK enables developers to find and book parking using the Arrive platform. Contained within the SDK are network requests and responses that can be used to access the Arrive API via native implementations as well as fully fleshed-out model objects. Each SDK function provides access to the API without the overhead of building integration from the ground up.

You will need an API key to use with the SDK. If you do not already have one, email partner-support@arrive.com to request credentials. Different API keys will correspond to production and sandbox environments. To use each environment, the proper key will need to match.

Setup

We provide two consumer options (dSYMs are included for all libraries).

  1. Manually download the SDK and use it as a fat library - https://s3.us-west-2.amazonaws.com/cdn.ios.parkwhiz.com/ios-sdk/{build_number}/Arrive.zip
    • Import files to the project.
    • Include library/all libraries at Target → General → Frameworks, Libraries, and Embedded Content.
    • In some cases, the library should be Embed & Sign.
  2. By CocoaPods - Our latest build's podspec is stored at - https://s3.us-west-2.amazonaws.com/cdn.ios.parkwhiz.com/ios-sdk/ArriveSDK.podspec However, if, for any reason, a specific build is needed, it could be found at - https://s3.us-west-2.amazonaws.com/cdn.ios.parkwhiz.com/ios-sdk/{build_number}/ArriveSDK.podspec
@podspec_path = 'https://s3.us-west-2.amazonaws.com/cdn.ios.parkwhiz.com/ios-sdk/ArriveSDK.podspec'

For All Libraries (Core+BLE) -

pod 'ArriveSDK', :podspec => @podspec_path

For Arrive Core API -

pod 'ArriveSDK/Core', :podspec => @podspec_path

Initialize an Arrive instance

import Arrive

let apiKey = "<#yourApiKey#>"
let configuration = ApiConfiguration(clientId: apiKey, environment: .sandbox)
self.arriveApi = ArriveApi(with: configuration)

Token Management

Before making any API calls, you need to get a public token.

Public Token

arriveApi.authenticationService.getPublicToken {
    response in
    print(response)
}

Authenticated Token

Most API calls require user authentication. To get a user authentication, use the SDK's AuthenticationService to authenticate a user.

arriveApi.authenticationService.authenticateUser(email: "<#username#>", password: "<#password#>") {
    response in
    switch response {
    case .success(let authorization):
        print(authorization)
    case .failure(let error):
        print(error)
    }
}

The authentication token is automatically stored and refreshed after a successful login API call. You can check the user's logged-in state:

arriveApi.authenticationService.isUserAuthenticated

Sample Calls

Get Locations

API reference

let coordinates = Coordinates(lat: 41.879072, lng: -87.6448557)
arriveApi.locationService.getLocations(near: coordinates, within: 0.5) {
    locations in
    ...
    //work with the returned array of locations
    ...
}

Add a Vehicle

API reference

arriveApi.vehicleService.addVehicle(plateNumber: "1B5 2R9",
                                    state: "CA",
                                    nickname: "The pickup truck",
                                    isDefault: true
) {
    vehicle in
    ...
    //work with the returned vehicle
    ...
}

Get Quotes

API reference

To search for parking around a geographic location, create a search (ParkingSearch) and use an instance of the ParkingSearchService from the SDK to execute the search.

Note: Parking searches only require a public token

let search = ParkingSearch.center(Coordinates(lat: 41.879072, lng: -87.6448557))
arriveApi.parkingSearchService.getQuotes(search: search) { (quotes) in
    ...
    //work with the returned array of quotes
    ...
}

Get a Ticket associated with a Booking

API reference

Note: You must pass in the booking's original authorization code. The authorization code can be found on the Booking model.

arriveApi.bookingService.getTicket(bookingId: 123, authorizationCode: "abc123") { result in
    switch result {
        case .success(let ticket):
            //work with the returned ticket
        case .failure(let error):
            //error
    }
}

ArriveBLE Docs

ArriveBLE allows interaction with Flash gates through the device's CoreBluetooth. It is a standalone framework, but parts of its logic depend on Arrive/Core SDK.

Every file that needs to use ArriveBLE logic must import it.

import ArriveBLE

Transient

Initialize a BLEGateManager instance

Create a BLEGateManager and conform to its delegate - BLEGateDelegate.

A BLEGateManager requires a BLEGateConfigurations object to configure the manager.

lazy var configurations = BLEGateConfigurations(
        passType: .transient(appId: ##APP_ID##, authKey: ##Auth_Key##),
        transactionId: ##TRANSACTION_ID##,
        log: true // (default is false)
)
  • appId - FlashParking provides your 20-character application ID.
  • authKey - FlashParking provides your 20-character authorization key.
  • transactionId - Transaction Id (or voucher code) that includes your prefix provided by FlashParking.
  • log: Enables logging recommended to disable on production.
BLEGateDelegate should conform to -
// Gate is ready for interaction with an iOS device.
// At this point, we start scanning.
func gateIsReady() {
        manager.startScan()
    }
    
    func gateSuccess(_: BLEGateMessage) {
        
    }
    
    func gateFailure(_ error: BLEGateResponse) {
        // see BLEGateResponse
    }
Valid Gate Responses
public enum BLEGateResponse: Int {
    case success = 1

    /// TransactionID been used
    case alreadyUsed

    /// TransactionID / MonthlyCustomerID / TicketID / KeyTag - NotFound
    case notFound

    case facilityFull

    case corruptedTicket

    case unknown

    /// TransactionID been expired
    case expired

    /// TransactionID isn't valid yet
    case tooEarly

    case unRecognizedAccountType

    /// Invalid AppKey and/or AuthKey
    case authentication

    case paymentError

    /// AppKey and/or AuthKey weren't injected
    case missingCredentials
    
    /// Bluetooth isn't enabled
    case bluetoothDisabled = 100

    /// Monthly - Device isn't registered
    case monthlyAccountNotFound

    /// No response from gate
    case timedOut

    case gateNotFound

    /// TransactionID wasn't injected
    case noAvailableToken
}

Transient Sample Code:

import UIKit
import CoreLocation
import ArriveBLE

class TransientViewController: UIViewController, BLEGateDelegate {
    
    // MARK: - Members
    
    var manager: BLEGateManager!
    
    lazy var configurations = BLEGateConfigurations(
        passType: .transient(appId: ##APP_ID##, authKey: ##Auth_Key##),
        transactionId: ##TRANSACTION_ID##,
        log: true
    )

    // MARK: - Methods
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        manager = BLEGateManager(
            configurations: configurations,
            delegate: self
        )
    }
    
    func gateIsReady() {
        manager.startScan()
    }
    
    func gateSuccess(_: BLEGateMessage) {
        // do any success logic
    }
    
    func gateFailure(_ error: BLEGateResponse) {
        // handle error
    }
    
    @IBAction func openTheGate() {
        manager.openGate()
    }
}

Enable Monthly

Initialize a BLEGateManager instance

Create a BLEGateManager and conform to its delegate - BLEGateDelegate. A BLEGateManager requires a BLEGateConfigurations object to configure the manager. Use passType: .monthly.

import ArriveBLE

class SomeClass: BLEGateDelegate  {
    // ...
    lazy var bleConfig = BLEGateConfigurations(passType: .monthly)
    lazy var manager = BLEGateManager(with: bleConfig, delegate: self)
    // ...
}

Start by registering the device associated with the user's Flash monthly account. The user can only register the phone number of whatever is tied to the SIM card in the phone. Follow the code below to bootstrap the onboarding process.

Start the registration process by calling register:

// ...
manager.register(self)
// ...

Note that register takes a UIViewController. This is required to present the MFMessageComposeViewController to the user to send a text message to associate the phone number with the current device.

  • If the customer is registered in the system and has at least one monthly parking location, gateSuccess will be invoked with BLEGateMessage equaling .authentication.
  • If no monthly locations are associated with the phone number, or the account does not exist, gateFailure will be invoked with an .authentication error. Use this to determine if the user has monthly parking enabled or not.
  • If the user cancels the registration process by dismissing the MFMessageComposeViewController, gateFailure will also be invoked with an .authentication error.

Open Gate

First, the device and customer must be registered - see Enable Monthly.

To open a gate within BLE range, ensure that the gate is ready, then call openGate():

if isReady {
    manager.openGate()
}
  • If the gate successfully opens, the gateSuccess function of BLEGateDelegate will be invoked.
  • If the gate fails to open or another error occurs, the gateFailure function of BLEGateDelegate will be invoked.

Start/Stop Scan

Initializing a BLEGateManager will automatically start scanning for gates, but it is possible to start scanning manually:

manager.startScan()

To stop scanning for gates, call:

manager.stopScan()

Get a QR Code

Kiosks allow for the scanning of a QR code to vend monthly customers. To generate the QR code string, ensure your passType for BLEGateManager is .monthly. After the device is registered (see Enable Monthly), the QR code is stored permanently and accessible as a property on the manager from there on out:

gateManager.qrCode
  • If the current passType is not .monthly, qrCode will be nil.
  • If the device has not been registered, qrCode will be nil.

Get Monthly Parking Status

Although a customer can't register for monthly parking without having at least one monthly location, it may be useful to check the monthly parking status before/after registration:

gateManager.isMonthlyRegistered

Monthly Pass Sample Code:

import UIKit
import ArriveBLE

class ViewController: UIViewController, BLEGateDelegate {
    lazy var bleConfig = BLEGateConfigurations(passType: .monthly)
    var manager: BLEGateManager!
    private var isReady = false

    // MARK: - Proposed Methods

    override func viewDidLoad() {
        super.viewDidLoad()
        manager = BLEGateManager(with: bleConfig, delegate: self)
    }

    @IBAction func register(_ sender: UIButton) {
        if !manager.isMonthlyRegistered {
            manager.register(self)
        }
    }

    @IBAction func openGate(_ sender: UIButton) {
        if isReady {
            manager.openGate()
        }
    }

    // MARK: - BLEGateDelegate

    func gateIsReady() {
        // Gate is discovered and ready.
        isReady = true
    }

    func gateSuccess(_ status: BLEGateMessage) {
        // Authenticated successfully, or gate opened.
        switch status {
            case .authentication:
                print("Authenticated successfully!")
            case .opened:
                print("Opened gate successfully!")
            default:
                break
        }
    }

    func gateFailure(_ error: BLEGateError) {
        // Issue with gate or authentication.
        switch error {
            case .authentication:
                print("User has no monthly locations!")
            default:
                print("Other error occurred.")
        }
    }
}
Last updated April 20, 2023