Skip to main content
The iOS SDK is the Swift package PlugchoiceSDK. It shows Link, the hosted flow at connect.plugchoice.com, in a sheet, and does what a web page can’t: join a charger’s Wi-Fi, find and reach chargers on the local network, talk Bluetooth to them and scan a QR code. It works with UIKit and SwiftUI.

Install

Requirements: iOS 16 or later, and Xcode 16 or later. On iOS 18 and later the SDK joins a charger’s Wi-Fi through AccessorySetupKit; on iOS 16 and 17 it uses the classic join prompt. Add the package with Swift Package Manager. In Xcode, choose File › Add Package Dependencies…, enter https://github.com/plugchoice/mobile-sdk, pick Up to Next Minor Version from 0.4.0, and add the PlugchoiceSDK library to your app target. In a Package.swift:
Package.swift
Then import it where you use it:

Configure your app

Capabilities

In Xcode, open your app target’s Signing & Capabilities and add: Your App ID needs the same capabilities in the Apple Developer portal.

Info.plist

Info.plist
Write the usage strings for your users: iOS shows them in its permission prompts. The SDK asks for each permission only when the flow needs it.
Don’t add Bluetooth to NSAccessorySetupKitSupports. It would limit CoreBluetooth in your whole app to accessories set up through AccessorySetupKit, and the SDK’s Bluetooth would stop working.

Create the SDK

Create one Plugchoice instance for your app’s lifetime. Give it a callback that fetches a client secret from your server:
The callback calls your own endpoint, which creates a client session scoped to the action and returns its secret. Get started shows that endpoint. In the app, it can look like this:
The SDK calls fetchClientSecret when Link opens, while the hosted flow loads. It calls it again, with the same action, whenever the secret expires (after 60 minutes), so a user can take as long as they need. The secret never goes into a URL, and the SDK never logs it. Open Link with an action, from a view controller:
In SwiftUI, use the plugchoiceLink modifier. Setting isPresented to false closes Link with cancelled.
The actions: The SDK passes the action to the hosted flow without reading it, so a new action works through .custom before it gets a helper. Your client session’s scope must cover the action’s charger or site. Actions explains each one. Link is a sheet the user can’t swipe away. It keeps the screen awake while it’s open, because a charger’s Wi-Fi connection drops when the phone locks.

Handle the result

The completion gets a LinkResult:
LinkResult.Status
.success, .cancelled or .error.
String
The action the link session did. The user may have picked another one from the list of what a charger can do; otherwise it’s the action you opened.
String?
The link session, when the hosted flow started one. nil when Link closed before that.
[Device]
The devices the link session reported, each { type, id }. On .success, the devices it finished. type is "charger" today; ignore types you don’t know.
LinkError?
On .error: a code, and a message for your logs.
The result is for your app’s UI. Before you rely on it, your server confirms it with Get a link session (GET /sdk/v1/link-sessions/{id}). Link sessions explains why.

Errors

On .error, result.error?.code says why. The SDK sets two codes itself: Every other code comes from the hosted flow. The one to know is clientSecretUnavailable: your fetchClientSecret threw, returned an empty string or took longer than 30 seconds. The user then sees this screen, with Try again, and the result reports clientSecretUnavailable if they close it. Other codes are for your logs; don’t branch on them. Errors lists them.

Show actions where they work

Not every charger can do every action, and not every phone has what an action needs. Show a button only when both are true:
  • The charger’s capability for it is capable. Your server reads it with Get a charger.
  • Every transport in the capability’s needs is in Plugchoice.transports().
Plugchoice.transports() asks for no permission. It returns:
  • wifi, http and socket: always.
  • lan: when your Info.plist declares at least one type in NSBonjourServices.
  • ble: on a device with Bluetooth LE (not the simulator), when your Info.plist has NSBluetoothAlwaysUsageDescription.

Test on a real device

The simulator can open Link and run the flow up to the charger, but it can’t reach one. Use an iPhone for:
  • joining a charger’s Wi-Fi, and the AccessorySetupKit prompt on iOS 18 and later
  • the Local Network prompt, and finding chargers on the home Wi-Fi
  • everything Bluetooth (the simulator has no Bluetooth)
  • scanning a QR code with the camera
The example app opens Link with a client secret you paste, for any action.