Streaming is available in most browsers,
and in the Developer app.
-
Meet AccessorySetupKit
Elevate your accessory setup experience with AccessorySetupKit. Display a beautiful pairing dialog with an image of your Bluetooth or Wi-Fi accessory — no trip to the Settings app required. Discover how to improve privacy by pairing only your app with an accessory. And learn how you can migrate existing accessories so they can be managed by AccessorySetupKit.
Resources
-
Download
Hi! I’m Yixin, and I work on the Bluetooth team at Apple.
Today, I’m excited to introduce to you a better accessory setup experience with AccessorySetupKit. We’ll first have an overview.
Then we will dive into the APIs and create a sample app together.
Finally, we’ll discuss how to make your accessories look great in the accessory picker.
Let’s start with an overview.
Accessories are an incredibly important part of our lives, from fitness trackers that monitor your health, to creative tools that bring your artistic visions to life. In iOS 18 and iPadOS 18, we created something to help you make the setup experience for your accessories even better.
Introducing AccessorySetupKit, a brand new framework that offers a streamlined and privacy-friendly approach for your app to set up and manage accessories with Bluetooth and Wi-Fi.
Simply call an API, and the brand-new accessory picker will appear with any nearby accessories that your app wants to pair with. You will see your own high-fidelity artwork and friendly name for the accessories in the picker.
With just one tap, the accessory is securely paired with your app and ready to use. This one tap will allow access to both Bluetooth and Wi-Fi from your accessory, if it supports both.
Your app can continue using the same CoreBluetooth and NetworkExtension APIs for communication with the accessory.
In Privacy & Security settings, the Accessories row is the new home for all accessories managed through AccessorySetupKit.
In the detail page of each accessory your product image, display name and the hardware name help people easily identify the accessory.
People can modify the accessory name, toggle the access of its paired apps, or remove it from the system.
The newly-designed accessory page can also be found in Bluetooth Settings and Wi-Fi Settings based on the technologies your accessory supports.
In iOS 18 and iPadOS 18, AccessorySetupKit supports Bluetooth and Wi-Fi accessories. So, how does it work? Let’s start by reviewing how apps used to set up accessories, and how AccessorySetupKit differs from it.
Up until now, an app would request access to use Bluetooth in order to discover and connect to any accessory.
A separate request would be required for the app to join a Wi-Fi network that the accessory is broadcasting.
And there could be additional Bluetooth pairing requests.
In comparison, AccessorySetupKit removes all the friction for both you and your customers.
With just one tap, the accessory is paired to your app, granting your app both Bluetooth and Wi-Fi access to it. And it's easier than ever for people to understand what they are allowing in the new accessory picker, as it displays the friendly name and product image for your accessory, as well as the hardware name the accessory advertises over the air, so that people can make sure of its authenticity before they tap to set up. This is all made possible with the privacy-friendly design of AccessorySetupKit.
The accessory picker shown on top of your app runs in a separate process. Your app sends over the discovery rules and assets to the picker process. These rules can be configured to have both Bluetooth and Wi-Fi interfaces, so you can obtain all necessary communication access to an accessory within one setup flow.
Once discovered, the accessory is presented in the picker, and can be paired with your app with just one tap.
And that’s an overview of AccessorySetupKit. Let’s now dive into the APIs. We will make an app that sets up digital dice and syncs roll results over Bluetooth, and you can download the sample project as well.
The digital dice are simulated by an app using CoreBluetooth. Our sample app will connect to it and subscribe to its Bluetooth GATT service.
To use AccessorySetupKit, we will add a few entries in the app’s Info.plist. We'll put Bluetooth under AccessorySetupKit - Supports since that's how our sample app talks to the dice. Wi-Fi is another supported item in this array.
We’ll then add the rules for the accessory’s discovery, whether it’s Bluetooth Services, Wi-Fi SSIDs, or Company Identifiers. Since our dice have 2 color variants and each advertises a different service UUID, we’ll put these 2 UUIDs under Bluetooth Services. Now, to use an accessory with your app, there are 3 steps: discovery of the accessory, authorization to use the accessory, and communication with the accessory. AccessorySetupKit takes care of discovery and authorization, while the communication is still handled with CoreBluetooth and NetworkExtension just like previous versions of iOS.
The first class we'll encounter in AccessorySetupKit is ASAccessorySession.
This is the central object that displays the accessory picker, notifies you of events, and manages accessories.
When we ask the ASAccessorySession to present the picker, we provide an array of ASPickerDisplayItem objects.
ASPickerDisplayItem defines the name and image for an accessory, as well as a discovery descriptor to tell the system how to scan for it.
With that, we can call showPicker on the ASAccessorySession to begin discovery and authorization in the picker process.
When the setup finishes, your app will receive an ASAccessoryEvent via the session object. The event contains important information about the newly-added accessory for your app to interact with it.
Now let’s write some code.
As we said, first we’ll create an ASAccessorySession.
The session is activated with a DispatchQueue where you want your API calls to be executed, and an eventHandler that your app uses to process events from the session.
An ASAccessoryEvent is passed to the event handler whenever there’s an update about your session, your accessories, or the picker. There’s a variety of events and you can differentiate them by checking their eventType. Use the activated event as the signal that allows you to query your accessories by reading the 'accessories' property from your session, or start a new setup process.
We’ll talk more about event handling later. Let’s now learn how to start a setup process by creating picker display items and invoking the accessory picker.
Creating the display item requires the product name, product image and a discovery descriptor. The ASDiscoveryDescriptor class lets you combine all the required discovery rules into one composite object. The system will match the scanned results with all rules inside the descriptor to filter for the exact accessory that you care about.
ASDiscoveryDescriptor is highly flexible with lots of customizable fields such as Bluetooth service UUID and Wi-Fi SSID. Learn all about it in our documentation on developer.apple.com.
To discover our dice, the discovery descriptor needs to have its bluetoothServiceUUID set as the GATT service UUID advertised by the dice.
And since the pink dice and blue dice advertise different service UUIDs based on their color, we want to create a different discovery descriptor for each color using their own service UUID.
We’ll then create the display items for these two color variants of our dice by passing in their name, image and corresponding discovery descriptor. This way the system will be able to scan for both color variants as separate accessories.
There are more properties of ASPickerDisplayItem that you can use to customize the setup experience. Make sure to check out our documentation.
And finally, we have a button in our sample app that simply calls showPicker on our activated session, while passing in the display items we created for the pink dice and blue dice. This is when the accessory picker appears and starts the discovery and setup process. Let’s give it a go.
Here on the desk I have a phone simulating pink dice.
In our sample app, if we tap the Add button, the accessory picker slides on top of the app, and the pink dice are instantly discovered and displayed. Now if I also turn on my blue dice using another simulator phone, notice it gets discovered and animated to peek through the edge of the screen. The picker creates a horizontal carousel to house the beautiful assets of your accessories. We can swipe left and right to select the dice we want to set up this time.
Hmm, they both look cute, but we all love a hot pink. Let’s set up the pink dice. Connecting… and done. Pink dice are now ready to use with our app. It’s that fast.
The accessory picker will show various Bluetooth-pairing UI if your accessory requires a more secure pairing method, such as numeric comparison and passkey entry.
Now that the pink dice are set up, what’s next? When an accessory is ready to use, the app would get an accessoryAdded event. By reading the accessory property from this event, we get an ASAccessory object that represents our newly-setup dice. Some other events include accessoryChanged, which is sent when properties of the accessory have changed, such as display name, which can be changed in Settings. accessoryRemoved is sent when the accessory was removed from the system, either by the user or your app.
The ASAccessory class represents a physical accessory. It has the accessory’s authorization state, display name, discovery descriptor, and all of its networking interfaces, including a Bluetooth identifier of the CBPeripheral, and Wi-Fi SSID if it broadcasts a Wi-Fi network. With the ASAccessory object from the accessoryAdded event, we can use CoreBluetooth to connect to the Bluetooth peripheral.
We’ll create a CBCentralManager as always. Since we declared AccessorySetupKit support in our Info.plist, unlike previous versions of iOS, people will not be asked for permission to use Bluetooth.
The CBCentralManager state will only become poweredOn when your app has paired accessories. In our case, we have our pink dice paired with AccessorySetupKit. We’ll retrieve the Bluetooth peripheral of the pink dice using bluetoothIdentifier. And of course, you can always use the central manager’s scan API to scan for peripherals. Only accessories paired with your app will be returned in the scan results. We’ll then connect to the peripheral. And once it’s connected, we’ll discover its GATT service, so that we can read and get notified of updates.
If you’re new to connecting Bluetooth accessories from your app, check out the CoreBluetooth documentation on developer.apple.com for more details. With all that, let’s try connecting to our newly setup pink dice! We are able to connect from our sample app to the dice simulator. Let’s bring out the simulator app and get rolling.
Nice! The roll results are synchronized into our sample app in real time.
And that is how we use AccessorySetupKit to set up an accessory, and later connect to it using existing networking APIs. But what if your app has already been authorized to use Bluetooth to manage existing accessories without AccessorySetupKit? How does the app transition from that permission model to the more fine-grained model in AccessorySetupKit? Using the Bluetooth peripheral identifiers or Wi-Fi SSIDs of your existing accessories, you can create instances of ASMigrationDisplayItem, which is a subclass of ASPickerDisplayItem. Pass these migration items to the showPicker call to upgrade the existing accessories to be managed by AccessorySetupKit and for them to be found in the new Accessories settings.
If the showPicker call contained only migration items, an informational page is shown on top of your app to notify people of the migration. Note that if you choose to pass migration items along with other non-migration display items, the migration will only happen when a new accessory is discovered and set up.
And that’s how you migrate existing accessories to be managed by AccessorySetupKit! Before we wrap up today’s session, I’d like to share some recommended design practices that can truly enhance your accessory setup experience.
The accessory picker offers a container box of 180 by 120 in point size to house your product image. Make sure your images have enough pixel resolution so they look sharp on screens of all scale factors.
Be sure to test how your assets look in both light mode and dark mode. The background of your image should be transparent so it looks great in both modes.
Utilize the transparent borders around your image. As the system scales the whole image to the container box, the wider the transparent borders are, the smaller the accessory appears on screen. Tune your transparent borders as a way of padding to get the perfect-sized image in the picker UI.
Keep in mind that part of your app’s UI is occluded by the accessory picker while people set up an accessory. Don’t make updates to your UI that might not be visible during setup. You can listen to the pickerDidPresent and pickerDidDismiss event to track the presentation and dismissal of the picker.
Last but not least, while the showPicker API is free to call anytime, to avoid unexpected picker presentation, we recommend showing people enough context about adding a new accessory before you show the picker. And if possible, bind the showPicker call to a button, so that the setup experience is always user-initiated.
Finally, I wanted to reiterate a few key points from today’s session. AccessorySetupKit is a brand new way to set up Bluetooth and Wi-Fi accessories. Your app gets both Bluetooth and Wi-Fi access to accessories with just one tap. It minimizes friction during a critical time — that is, when people first experience your product.
To use it, add AccessorySetupKit items to your app’s Info.plist. Create an ASAccessorySession and wait for its activation to start your tasks. Create an ASPickerDisplayItem for each accessory you want to discover. Show the accessory picker to start your setup process and respond appropriately to events.
And if your app already manages existing accessories using system access, use ASMigrationDisplayItem to migrate the accessories to AccessorySetupKit. Pay attention to the design of your assets so your accessories can be presented to your customers in the best ways possible inside the accessory picker.
Thanks for watching.
-
-
6:02 - Register event handler
import AccessorySetupKit // Create a session var session = ASAccessorySession() // Activate session with event handler session.activate(on: DispatchQueue.main, eventHandler: handleSessionEvent(event:)) // Handle event func handleSessionEvent(event: ASAccessoryEvent) { switch event.eventType { case .activated: print("Session is activated and ready to use") print(session.accessories) default: print("Received event type \(event.eventType)") } }
-
7:23 - Create picker display items
// Create descriptor for pink dice let pinkDescriptor = ASDiscoveryDescriptor() pinkDescriptor.bluetoothServiceUUID = pinkUUID // Create descriptor for blue dice let blueDescriptor = ASDiscoveryDescriptor() blueDescriptor.bluetoothServiceUUID = blueUUID // Create picker display items let pinkDisplayItem = ASPickerDisplayItem( name: "Pink Dice", productImage: UIImage(named: "pink")!, descriptor: pinkDescriptor ) let blueDisplayItem = ASPickerDisplayItem( name: "Blue Dice", productImage: UIImage(named: "blue")!, descriptor: blueDescriptor )
-
8:10 - Show picker
// Invoke accessory picker Button { session.showPicker(for: [pinkDisplayItem, blueDisplayItem]) { error in if let error { // Handle error } } } label: { Text("Add Dice") }
-
9:26 - Handle events
// Handle event func handleSessionEvent(event: ASAccessoryEvent) { switch event.eventType { case .accessoryAdded: let newDice: ASAccessory = event.accessory! case .accessoryChanged: print("Accessory properties changed") case .accessoryRemoved: print("Accessory removed from system") default: print("Received event with type: \(event.eventType)") } }
-
10:22 - Communicate with accessory
// Connect to accessory using CoreBluetooth let central = CBCentralManager(delegate: self, queue: nil) func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: // state will only be updated to poweredOn when you have paired accessories let peripheral = central.retrievePeripherals(withIdentifiers: [newDice.bluetoothIdentifier]).first central.connect(peripheral) default: print("Received event type \(event.eventType)") } } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self peripheral.discoverServices(pinkUUID) }
-
11:58 - Migrate existing accessories
// Create migration items let pinkMigration = ASMigrationDisplayItem(name: "Pink Dice", productImage: UIImage(named: "pink")!, descriptor: pinkDescriptor) pinkMigration.peripheralIdentifier = pinkPeripheral.identifier // Present picker with migration items session.showPicker(for: [pinkMigration]) { error in if let error { // Handle error } }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.