在 SwiftUI 中使用 WCSession 向 Apple Watch 发送消息

Posted

技术标签:

【中文标题】在 SwiftUI 中使用 WCSession 向 Apple Watch 发送消息【英文标题】:Send Message to Apple Watch using WCSession in SwiftUI 【发布时间】:2020-01-18 19:36:54 【问题描述】:

我很久以前做过一个例子,如何使用 Swift 从 iPhone 向 Apple Watch 发送简单的消息:

import UIKit
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate 

    // MARK: Outlets

    @IBOutlet weak var textField: UITextField!

    // MARK: Variables

    var wcSession : WCSession! = nil

    // MARK: Overrides

    override func viewDidLoad() 
        super.viewDidLoad()

        wcSession = WCSession.default
        wcSession.delegate = self
        wcSession.activate()

    

    // MARK: Button Actions

    @IBAction func sendText(_ sender: Any) 

        let txt = textField.text!
        let message = ["message":txt]

        wcSession.sendMessage(message, replyHandler: nil)  (error) in

            print(error.localizedDescription)

        

    

    // MARK: WCSession Methods
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) 

        // Code

    

    func sessionDidBecomeInactive(_ session: WCSession) 

        // Code

    

    func sessionDidDeactivate(_ session: WCSession) 

        // Code

    


现在我正在尝试使用 SwiftUI 做同样的事情,但到目前为止没有成功。 谁能帮忙解决这个问题?

我只需要知道如何在 SwiftUI 中使用 WCSession 类和 WCSessionDelegate。

谢谢

【问题讨论】:

【参考方案1】:

我刚刚和你有同样的问题,我想通了:

首先你需要实现一个符合WCSessionDelegate的类。我喜欢为此使用一个单独的类:

import WatchConnectivity

class ConnectivityProvider: NSObject, WCSessionDelegate 
    
    private let session: WCSession
    
    init(session: WCSession = .default) 
        self.session = session
        super.init()
        self.session.delegate = self
    
    
    func send(message: [String:Any]) -> Void 
        session.sendMessage(message, replyHandler: nil)  (error) in
            print(error.localizedDescription)
        
    
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) 
        // code
    
    
    func sessionDidBecomeInactive(_ session: WCSession) 
        // code
    
    
    func sessionDidDeactivate(_ session: WCSession) 
        // code
    

现在您需要一个将您的 ConnectivityProvider 作为参数的 ViewModel。 ViewModel 将负责您的 View 和 ConnectivityProvider 的连接。它还保存稍后在您的视图中定义的文本字段的值。

import SwiftUI  

final class ViewModel: ObservableObject 
    
    private(set) var connectivityProvider: ConnectivityProvider
    var textFieldValue: String = ""
    
    init(connectivityProvider: ConnectivityProvider) 
        self.connectivityProvider = connectivityProvider
    
    
    func sendMessage() -> Void 
        let txt = textFieldValue
        let message = ["message":txt]
        connectivityProvider.send(message: message)
    

现在您可以构建一个由文本字段和按钮组成的简单视图。您的 View 将依赖于您刚刚定义的 ViewModel。

import SwiftUI

struct ContentView: View 
    
    @ObservedObject var viewModel: ViewModel
    
    var body: some View 
        VStack 
            TextField("Message Content", text: $viewModel.textFieldValue)
            
            Button(action: 
                self.viewModel.sendMessage()
            ) 
                Text("Send Message")
            
        
    

最后但同样重要的是,您需要在 SceneDelegate 中组合 ConnectivityProvider、ViewModel 和 View:

let viewModel = ViewModel(connectivityProvider: ConnectivityProvider())
let contentView = ContentView(viewModel: viewModel)

...

window.rootViewController = UIHostingController(rootView: contentView)

===================================

更新:如何激活会话?

首先向您的 ConnectivityProvider 添加一个激活会话的新函数:

class ConnectivityProvider: NSObject, WCSessionDelegate 
    ...
    func connect() 
        guard WCSession.isSupported() else 
            print("WCSession is not supported")
            return
        
       
        session.activate()
    
    ...

现在您可以在需要连接 WCSession 时调用 connect 函数。您应该能够在任何地方连接它,例如在您的 SceneDelegate、您的 ViewModel 中,甚至直接在您的 ConnectivityProvider 的 init 中:

ConnectivityProvider 初始化:

class ConnectivityProvider: NSObject, WCSessionDelegate 

    private let session: WCSession

    init(session: WCSession = .default) 
        self.session = session
        super.init()
        self.session.delegate = self
        self.connect()
    
    ...

视图模型:

import SwiftUI  

final class ViewModel: ObservableObject 

    private(set) var connectivityProvider: ConnectivityProvider
    var textFieldValue: String = ""

    init(connectivityProvider: ConnectivityProvider) 
        self.connectivityProvider = connectivityProvider
        self.connectivityProvider.connect()
    

    func sendMessage() -> Void 
        let txt = textFieldValue
        let message = ["message":txt]
        connectivityProvider.send(message: message)
    

场景代理:

let connectivityProvider = ConnectivityProvider()
connectivityProvider.connect()
let viewModel = ViewModel(connectivityProvider: connectivityProvider)
let contentView = ContentView(viewModel: viewModel)

...

window.rootViewController = UIHostingController(rootView: contentView)

【讨论】:

什么时候激活会话? 我确实向我的 ConnectivityProvider 添加了一个连接函数,并直接在我的 SceneDelegate 中调用它。但是您可以在需要的地方调用它,例如在 ConnectivityProvider 的 init 函数内部。 func connect() guard WCSession.isSupported() else print("WCSession is not supported") return session.activate() 你有使用 swiftUI 的工作代码吗?我一直很难让手表从我的手机接收消息。我可以使用苹果提供的 SimpleWatchConnectivity 项目,但它使用故事板。如果有一个 SwiftUI 的基本起点就好了。感觉这里少了点什么,不是要给手表设置接收功能吗?在上面的代码中,没有关于如何处理手表扩展的方向。 您是仅使用 SwiftUI 还是使用带有 App 和 SceneDelegates 的 SwiftUI? 我终于明白了! ConnectivityProvider 在我的 ios 应用程序中。我与扩展共享了目标成员资格,并在我的 Watch Extension 的 ContentView 中对其进行了初始化。非常棘手,但我相信您的指导,谢谢您的先生。

以上是关于在 SwiftUI 中使用 WCSession 向 Apple Watch 发送消息的主要内容,如果未能解决你的问题,请参考以下文章

watchOS 3 在设备和手表之间共享的类中使用 WCSession

Xcode 9.2 Objective-C 与 WCSession 的链接器错误

在 Apple Watch 和 iPhone 之间使用 WCSession 共享数据

当watchOS应用程序使用HKWorkoutSession在后台运行时如何使WCSession可访问

将 WCSession 的委托设置为 nil

WCSession:使用 transferUserInfo 或 sendMessage 的最佳方式?