WatchConnectivity - 从手表配套应用程序在 iOS 设备上启动 AVPlayer

问题描述 投票:0回答:1

假设我有 SwiftUI 应用程序,它开始从列表中选定的 url 播放音频:

struct ContentView: View {
    
    @ObservedObject var viewModel: ContentViewModel
    
    var body: some View {
        List(PlayerLinks.links, id: \.self) { link in
            Button(link) {
                viewModel.play(from: link)
            }
        }
    }
}

class ContentViewModel: ObservableObject {
    
    let player = Player()
    
    init() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(notificationAction),
            name: .newLinkChosen,
            object: nil
        )
    }
    
    func play(from url: String) {
        player.play(from: url)
    }
    
    @objc func notificationAction(_ notification: Notification) {
        if let userInfo = notification.userInfo,
           let newURL = userInfo["newLink"] as? String {
            self.play(from: newURL)
        }
    }
}

class Player {
    
    var player: AVPlayer?
    
    init() {
        do {
            try AVAudioSession.sharedInstance()
                .setCategory(
                    AVAudioSession.Category.playback,
                    mode: AVAudioSession.Mode.default,
                    options: []
                )
        } catch let error as NSError {
            print(error.localizedDescription)
        }
    }
    
    func play(from url: String) {
        let playerItem = AVPlayerItem(url: URL(string: url)!)
        player = AVPlayer(playerItem: playerItem)
        player?.play()
    }
}

这个应用程序有 watchOS 配套应用程序,它也有一个电台列表:

struct ContentWatchView: View {
    
    @ObservedObject var viewModel: ContentWatchViewModel
    
    var body: some View {
        List(PlayerLinks.links, id: \.self) { link in
            Button(link) {
                viewModel.onRowSelected(url: link)
            }
        }
    }
}

class ContentWatchViewModel: ObservableObject {
    
    var connectivityManager: WatchConnectivityManager
    
    init(connectivityManager: WatchConnectivityManager) {
        self.connectivityManager = connectivityManager
    }
    
    func onRowSelected(url: String) {
        connectivityManager.sendPlayerLinkToIOS(url)
    }
}

通过点击 watchOS 应用程序中的行,我需要在 iOS 设备上启动 AVPlayer 播放。

为此,我实现了

WatchConnectivityManager
来处理 watchOS 和 iOS 应用程序之间的通信:

class WatchConnectivityManager: NSObject, ObservableObject, WCSessionDelegate {
    
    private let session: WCSession = WCSession.default
    var isReachable = false
    
    static var shared = WatchConnectivityManager()

    override init() {
        super.init()
        if WCSession.isSupported() {
            session.delegate = self
            session.activate()
        }
    }
    
    func session(
        _ session: WCSession,
        activationDidCompleteWith activationState: WCSessionActivationState,
        error: Error?
    ) {
        #if os(iOS)
        print("ACTIVATED ON IOS")
        #elseif os(watchOS)
        print("ACTIVATED ON WATCHOS")
        #endif
        DispatchQueue.main.async {
            self.isReachable = session.isReachable
        }
    }
    
    func sessionReachabilityDidChange(_ session: WCSession) {
        DispatchQueue.main.async {
            self.isReachable = session.isReachable
        }
    }
    
    #if os(iOS)
    func sessionDidDeactivate(_ session: WCSession) {
        session.activate()
    }

    func sessionDidBecomeInactive(_ session: WCSession) {
        print("Session did become inactive: \(session.activationState.rawValue)")
    }

    func sessionWatchStateDidChange(_ session: WCSession) {
        print("Session watch state did change: \(session.activationState.rawValue)")
    }
    #endif
    
    // MARK: MESSAGE RECEIVER
    func session(
        _ session: WCSession,
        didReceiveMessage message: [String : Any],
        replyHandler: @escaping ([String : Any]) -> Void
    ) {
        #if os(iOS)
        if let action = message["action"] as? String,
           action == "newPlayerLinkChosen",
           let link = message["link"] as? String {
            DispatchQueue.main.async {
                NotificationCenter.default.post(
                    name: .newLinkChosen,
                    object: nil,
                    userInfo: ["newLink": link]
                )
                replyHandler(["success": true])
            }
        } else {
            replyHandler(["success": false])
        }
        #endif
    }
    
    // MARK: MESSAGE SENDERS
    
    #if os(watchOS)
    func sendPlayerLinkToIOS(_ link: String) {
        let message = [
            "action": "newPlayerLinkChosen",
            "link": link
        ]

        session.sendMessage(message) { replyHandler in
            print(replyHandler)
        } errorHandler: { error in
            print(error.localizedDescription)
        }
    }
    #endif
}

extension Notification.Name {
    
    static let newLinkChosen = Notification.Name("NewLinkChosen")
}

sendPlayerLinkToIOS
func 发送带有所选链接的消息,该消息由
MESSAGE RECEIVER
方法接收,然后将带有所选链接的通知发布到
NotificationCenter.default
。 iOS
ContentViewModel
收到此通知,然后播放器启动。

当 iOS 应用程序位于前台时一切正常,但是当我们转到主屏幕并再次从手表应用程序中选择某个 url 时,它就不起作用了(iOS 应用程序未终止)。

这里是日志,如果它们对某人有帮助的话:

ACTIVATED ON IOS

2023-08-18 16:23:38.651377+0300 RemotePlayer[20479:6162721] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x6000022fce00> F8BB1C28-BAE8-11D6-9C31-00039315CD46

2023-08-18 16:23:52.046769+0300 RemotePlayer[20479:6163115] [AMCP]   4611          HALC_ProxyIOContext.cpp:783   HALC_ProxyIOContext::_StartIO(): Client running as an adaptive unboosted daemon

2023-08-18 16:23:52.047123+0300 RemotePlayer[20479:6163115]             HALPlugIn.cpp:519    HALPlugIn::StartIOProc: got an error from the plug-in routine, Error: 1852797029 (nope)

2023-08-18 16:23:52.048409+0300 RemotePlayer[20479:6163115] [aqme]                AQMEIO.cpp:211   error 1852797029

2023-08-18 16:23:52.049634+0300 RemotePlayer[20479:6163115] [aqme]  MEDeviceStreamClient.cpp:431   AQME Default-InputOutput: client stopping after failed start: <CA_UISoundClientBase@0x149514830>; running count now 0

2023-08-18 16:23:52.050233+0300 RemotePlayer[20479:6163115]      CA_UISoundClient.cpp:285   CA_UISoundClientBase::StartPlaying: AddRunningClient failed (status = 1852797029).
2023-08-18 16:23:53.738535+0300 RemotePlayer[20479:6163570] [AMCP]  59139          HALC_ProxyIOContext.cpp:783   HALC_ProxyIOContext::_StartIO(): Client running as an adaptive unboosted daemon

2023-08-18 16:23:53.741183+0300 RemotePlayer[20479:6163570]             HALPlugIn.cpp:519    HALPlugIn::StartIOProc: got an error from the plug-in routine, Error: 1852797029 (nope)

2023-08-18 16:23:53.744653+0300 RemotePlayer[20479:6163570] [aqme]                AQMEIO.cpp:211   error 1852797029

2023-08-18 16:23:53.745684+0300 RemotePlayer[20479:6163570] [aqme]  MEDeviceStreamClient.cpp:431   AQME Default-InputOutput: client stopping after failed start: <AudioQueueObject@0x14a809000; Unknown figplayer; [20479]; play>; running count now 0
ios swift watchkit watchconnectivity
1个回答
0
投票

刚刚给自己买了Apple Watch并再次测试。当应用程序在后台以及根本未打开应用程序时,它工作正常。由于某种原因,这不适用于模拟器...

© www.soinside.com 2019 - 2024. All rights reserved.