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