假设我有一款SwiftUI应用程序,它可以从列表中 Select 的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应用程序中的ROW,我需要在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
FFC发送具有选定链接的消息,该消息由MESSAGE RECEIVER
方法接收,然后它将具有选定链接的通知发布到NotificationCenter.default
.IOS ContentViewModel
接收到该通知,然后播放器启动.
当iOS应用程序在前台时,一切都运行良好,however it's not working when we go to the Home Screen and choose some url from watch app again (the iOS app is not terminated).
以下是日志(log),如果它们对某人有帮助的话:
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