Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Best way to pass a HomeKit or Matter setup code to the Home App Programatically

Apologies in advance for the long post. I'm new to HomeKit and Matter but not to development, I'm trying to write a SwiftUI app for my smart home to store all of my HomeKit and Matter setup barcodes along with other bits of information.

The intention is to scan the QR codes with my App and then save that QR payload in a simple Database along with other manually entered device details. Example payloads:

X-HM://00GWIN0B5PHPG    <-- Eufy V120 HomeKit Camera
MT:GE.01-C-03FOPP6B110  <-- Moes GU10 Matter Bulb

I have it 99% working, my app is even able to discern the manual pairing code from the above payloads. However one of the key feature of this is that I want to open a device entry in my app and tap the HomeKit or Matter code displayed in my app and and either:

a) Ideally pass it off to the Apple Home app to initiate pairing just like the native Camera App can.

b) Create a custom flow in my app using the HomeKit or Matter API's to initiate paring from within my app.

So ideally just like the flow that happens when you scan a setup QR with the normal camera and tap "Open in Home". However I want to trigger this flow with just knowing the Payload and not with scanning it via the camera.

I was hoping there might be something as simple as a URL scheme that I could call with the payload as a variable and it then deep links and switches to the Home app, but I haven't found any info relating to this that actually works.

This is some code I have tried with the HomeKit API but this also results in an error:

    import HomeKit
   
    func startHomePairing(with setupCode: String) {
        // Handle HomeKit setup 
        guard let payload = HMAccessorySetupPayload(url: URL(string: setupCode)!) else {
            print("Invalid HomeKit setup code or format.")
            return
        }
        
        let setupRequest = HMAccessorySetupRequest()
        setupRequest.payload = payload
        
        let setupManager = HMAccessorySetupManager()
        
        // Perform the setup request and handle the result
        setupManager.performAccessorySetup(using: setupRequest) { result, error in
            if let error = error {
                // Error handling: print the error details
                print("Error starting setup: \(error.localizedDescription)")
                // Print more details for debugging
                print("Full Error: \(error)")
            } else {
                // Success: pairing was successful
                print("Successfully launched Home app for HomeKit setup.")
            }
        }
    }

But when passing in the QR payloads above it give the following ..

HomeKit Code

[0CAB3B05] Failed to perform accessory setup using request: Error Domain=HMErrorDomain Code=17 "(null)"

Matter Code

Failed to create HMSetupAccessoryPayload from setup payload URL MT:GE.01-C-03FOPP6B110: Error Domain=HMErrorDomain Code=3 "(null)"

I have added the "HomeKit" and "Matter Allow Setup Payload" capabilities to my app, I have also ensured I have these in the .plist ..

<key>NSHomeKitUsageDescription</key>
<string>Access required to HomeKit to initiate pairing for new accessories</string>

I also added a call to ensure my app appears in the Settings / Privacy / HomeKit section. I originally thought was a seemingly simple task, but I am really struggling with how to make it work!

Answered by DTS Engineer in 836556022

Apologies in advance for the long post.

Having hit the 7000 word count on more than one occasion, you have nothing to be ashamed of. My only concern is that my reply may not live up to the standard you've set.

SO, let me start with the errors you've been getting from HomeKit:

HomeKit Code Domain=HMErrorDomain Code=17

That would be "HMErrorCodeInsufficientPrivileges", which then leads to here:

So ideally just like the flow that happens when you scan a setup QR with the normal camera and tap "Open in Home". However I want to trigger this flow with just knowing the Payload and not with scanning it via the camera.

Unfortunately, the answer here is basically "no, that's not possible", at least for HAP accessories. The issue here is actually that part of the security model of HAP is that acquiring the setup code should require some level of physical access to the accessory. For example, the specification requires that the setup code NOT be stored, accessible, or derivable by the accessory itself.

There is an entitlement that allows that requirement to be bypassed but it's managed by the MFi program and it's generally only granted to specific hardware vendors who are either:

  • Adding HomeKit support to an already shipping accessory, so there isn't any HomeKit setup code to scan.

  • Creating an accessory which has some kind of "inherent" physical limitation which would make the standard pairing process cumbersome/awkward.

Switching to the matter side:

Matter Code Error Domain=HMErrorDomain Code=3

That would be "HMErrorCodeInvalidParameter", which is what I think you'd get if you passed a matter payload into payload. The matter payload has a different format, which would go through matterPayload.

I haven't specifically tried it, but I'd expect it to work fine. While Matter has a physical security model similar to HomeKit ("photograph QR codes to prove access"), the specification is much more open and we don't enforce that security in the same way HomeKit does.

One quick comment on this:

b) Create a custom flow in my app using the HomeKit or Matter API's to initiate paring from within my app.

On the HomeKit side, I would STRONGLY caution you against trying to do "manual" HomeKit pairing. The history here is a bit complicated but the HomeKit framework was actually introduced in iOS 8.0 while Home.app wasn't released until iOS 10.0. Because of that, there is an "original" pairing process which does not use the system UI and which is somewhat under programmatic control. That flow has also never ACTUALLY been deprecated and probably does sort of work in iOS 18.

However, that flow is a horrible pain to work with. For example, WAC and HAP pairing are handled through separate discovery and pairing process, which is exactly as annoying and error prone as it sounds. In fact, the ENTIRE reason "addAndSetupAccessoriesWithCompletionHandler" and the variants we've since replaced it with were created is because the original flow was so painful. Similarly, the main reason the old flow hasn't been deprecated is simply that developers migrated away from the older flow so quickly that we never felt an real need to "push" developers toward the new API.

In any case, I wanted to warn you about this because if you dig into the API enough you can probably what looks like our special secret manual pairing flow. It's isn't secret or special, just old and unpleasant.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Apologies in advance for the long post.

Having hit the 7000 word count on more than one occasion, you have nothing to be ashamed of. My only concern is that my reply may not live up to the standard you've set.

SO, let me start with the errors you've been getting from HomeKit:

HomeKit Code Domain=HMErrorDomain Code=17

That would be "HMErrorCodeInsufficientPrivileges", which then leads to here:

So ideally just like the flow that happens when you scan a setup QR with the normal camera and tap "Open in Home". However I want to trigger this flow with just knowing the Payload and not with scanning it via the camera.

Unfortunately, the answer here is basically "no, that's not possible", at least for HAP accessories. The issue here is actually that part of the security model of HAP is that acquiring the setup code should require some level of physical access to the accessory. For example, the specification requires that the setup code NOT be stored, accessible, or derivable by the accessory itself.

There is an entitlement that allows that requirement to be bypassed but it's managed by the MFi program and it's generally only granted to specific hardware vendors who are either:

  • Adding HomeKit support to an already shipping accessory, so there isn't any HomeKit setup code to scan.

  • Creating an accessory which has some kind of "inherent" physical limitation which would make the standard pairing process cumbersome/awkward.

Switching to the matter side:

Matter Code Error Domain=HMErrorDomain Code=3

That would be "HMErrorCodeInvalidParameter", which is what I think you'd get if you passed a matter payload into payload. The matter payload has a different format, which would go through matterPayload.

I haven't specifically tried it, but I'd expect it to work fine. While Matter has a physical security model similar to HomeKit ("photograph QR codes to prove access"), the specification is much more open and we don't enforce that security in the same way HomeKit does.

One quick comment on this:

b) Create a custom flow in my app using the HomeKit or Matter API's to initiate paring from within my app.

On the HomeKit side, I would STRONGLY caution you against trying to do "manual" HomeKit pairing. The history here is a bit complicated but the HomeKit framework was actually introduced in iOS 8.0 while Home.app wasn't released until iOS 10.0. Because of that, there is an "original" pairing process which does not use the system UI and which is somewhat under programmatic control. That flow has also never ACTUALLY been deprecated and probably does sort of work in iOS 18.

However, that flow is a horrible pain to work with. For example, WAC and HAP pairing are handled through separate discovery and pairing process, which is exactly as annoying and error prone as it sounds. In fact, the ENTIRE reason "addAndSetupAccessoriesWithCompletionHandler" and the variants we've since replaced it with were created is because the original flow was so painful. Similarly, the main reason the old flow hasn't been deprecated is simply that developers migrated away from the older flow so quickly that we never felt an real need to "push" developers toward the new API.

In any case, I wanted to warn you about this because if you dig into the API enough you can probably what looks like our special secret manual pairing flow. It's isn't secret or special, just old and unpleasant.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I plan to ignore HomeKit barcodes for the moment, so I'm now concentrating on the Matter side of things. However I cannot get Xcode to recognise MTRSetupPayload even after importing HomeKit and MatterSupport I just get "Cannot find 'MTRSetupPayload' in scope"

After rummaging around in the documentation links you provided I did come across this:

Apple Developer Onboarding A Matter Device

Which looks to be an example of almost exactly what I am trying to achieve, however it's outdated and a lot of the references are deprecated. When trying to modify this with the new methods I again come across the issue of being unable to declare MTRSetupPayload.

Accepted Answer

I plan to ignore HomeKit barcodes for the moment, so I'm now concentrating on the Matter side of things. However I cannot get Xcode to recognise MTRSetupPayload even after importing HomeKit and MatterSupport I just get "Cannot find 'MTRSetupPayload' in scope"

The MatterSupport framework is actually a more specialized framework which is used to implement the MatterSupport extension that allows a matter accessory to be paired into multiple echosystems. MTRSetupPayload is declared in the Matter framework, which is what actually implements direct matter accessory control.

Which looks to be an example of almost exactly what I am trying to achieve, however it's outdated and a lot of the references are deprecated.

The matter framework is being actively developed in a very public way and that lead to a burst of APIs that were introduced and then renamed/redesigned, leading to the original API being "deprecated" very quickly. On the other hand, this does mean that you can actually see the code for MTRSetupPayload (or any other matter framework API) if you want to.

When trying to modify this with the new methods I again come across the issue of being unable to declare MTRSetupPayload.

I think all you'd need to do is:

import Matter
...
{
...
	let payload = MTRSetupPayload.init(payload: string)
...
}  

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Excellent thank you for you replies, I haven't 100% tested this yet, but using a valid matter setup QR the below code launches the "Add Device to Home UI" and accepts the code, recognises the device type etc ..

import HomeKit
import Matter

func registerMatterDevice(onboardingCode: String) async {

        let payload = MTRSetupPayload(payload: onboardingCode)
 
        guard let setupPayload = payload else {
            print("Invalid Matter onboarding code")
            return
        }

        let request = HMAccessorySetupRequest()
        request.matterPayload = setupPayload

        let setupManager = HMAccessorySetupManager()
        _ = try? await setupManager.performAccessorySetup(using: request)
        
}

This will hopefully let me get to where I want to be. Thank you very much.

Best way to pass a HomeKit or Matter setup code to the Home App Programatically
 
 
Q