EA showBluetoothAccessoryPicker not showing in SwiftUI
Asked Answered
P

2

2

I have created a small program that needs to connect to an external accessory. I have been able to do so successfully with UIKit and the EA framework.

The problem I am having is that I have a SwiftUI based app that needs to use the External Accessory but when I call the showBluetoothAccessoryPicker function, the picker window does not show.

What I have tried so far:

I have read that you need to have the app delegate with

var window: UIWin

setup correctly to see the picker window. So I created a class

class AppDelegate: UIApplicationDelegate
{
    var window: UIWindow?
}

I then associated the app delegate with my swiftui "App" struct like this:

@main
struct POC: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            TabSelectionView()
        }
    }
}

Nevertheless, when I call showBluetoothAccessoryPicker the picker window does not show.

I see the following in the output log:

2021-09-20 13:16:31.804796-0500 POC[2150:1514447] IAPDHasLaunched: kIAPAvailableNotification iapdAvailableState 0 -> 0
2021-09-20 13:16:31.805791-0500 POC[2150:1514447] IAP2DHasLaunched: kIAP2AvailableNotification iap2dAvailableState 0 -> 0
2021-09-20 13:16:31.807219-0500 POC[2150:1514447] -[EAAccessoryManager _initFromSingletonCreationMethod] isRunningOnMac

This log appears correct and matches the output from a working demo app that uses UIKit. It's just that the picker window does not show.

Does anybody know what needs to be done to get the picker window to appear correctly for SwiftUI apps? Is there any SwiftUI examples of connecting to an External Accessory that anybody could point me towards?

Thanks in advance for any help.

Plectrum answered 20/9, 2021 at 18:48 Comment(5)
Having the same problem here… did you find a workaround yet? Did you report the problem via Feedback Reporter?Sherikasherill
I did not find a solution for SwiftUI based app. I eventually gave up and switched to a UIKit App. This did not work straight out of the box and also needed some modification to get to work. I will update with that info when I have access again.Plectrum
That would be cool! I can see the picker now, but it's empty… although all my info is correct and I can talk to the EA. So there must be something else missing.Sherikasherill
If you can see the picker, then it is working. You may need to verify that you included your protocol in the Supported External Accessory Protocols in the Info.plist.Plectrum
I know, but all that is correct. If it wasn't in the plist, I couldn't connect and talk to the EA in the first place. I'm afraid it's a bug in the picker.Sherikasherill
S
4

UPDATE: It's also broken in UIKit since iOS 13! Feedback reported as FB9856371.

Apparently the UIScene-based mechanism (introduced in iOS13) kills it (and since SwiftUI is based on the new scene-based mechanism, it only looks like it's a problem with SwiftUI).

If you roll back to the UIApplicationDelegate-based lifecycle, it works again.

OLD ANSWER:

It's just broken in SwiftUI right now.

Further experimentation shows that the EAExternalAccessory picker seems to query the UIApplicationDelegate's window property to find out where to display itself in.

Alas, that being an optional property, which is not implemented in the SwiftUI's version of the AppDelegate, it only gets nil and does not show up.

It also seems that the @UIApplicationDelegateAdaptor approach does not work in this case, because it does not really act as a stand-in for the real app delegate, but rather gets the methods forwarded.

A way to work around this is to use the UIKit application lifecycle and present your SwiftUI app via the UIHostingController.

It might also be possible to hack this via method swizzling, but if so, I don't know how.

Please open a bug report.

Sherikasherill answered 23/1, 2022 at 15:14 Comment(0)
D
1

Thanks DrMickeyLauer, I had the same issue. UIApplicationDelegate-based lifecycle is the current solution until Apple has fixed it.

The fastest way to get this working is the following.

  1. Comment out the @main annotation from your struct implementing the App protocol. Alternatively delete the file.
import SwiftUI

// @main
struct iOSApp: App {
    // ...
}

  1. Create a AppDelegate class with the minimum needed to launch your root SwiftUI view.
import UIKit
import SwiftUI

@main
class AppDelegate: NSObject, UIApplicationDelegate {
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = UIHostingController(rootView: SwiftUiView())
        window?.makeKeyAndVisible()
        return true
    }
}
Durable answered 6/9, 2023 at 11:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.