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

Max*_*Max 8 watchos swiftui

我很久以前做过一个例子,如何使用 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

    }

}
Run Code Online (Sandbox Code Playgroud)

现在我正在尝试使用 SwiftUI 做同样的事情,但到目前为止没有成功。任何人都可以帮助解决这个问题吗?

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

谢谢

Kev*_*inP 15

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

首先,您需要实现一个符合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
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您需要一个 ViewModel 将您的 ConnectivityProvider 作为参数。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)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以构建一个简单的视图,它由一个文本字段和一个按钮组成。您的 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")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

...

window.rootViewController = UIHostingController(rootView: contentView)
Run Code Online (Sandbox Code Playgroud)

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

更新:如何激活会话?

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

class ConnectivityProvider: NSObject, WCSessionDelegate {
    ...
    func connect() {
        guard WCSession.isSupported() else {
            print("WCSession is not supported")
            return
        }
       
        session.activate()
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

连接提供者初始化:

class ConnectivityProvider: NSObject, WCSessionDelegate {

    private let session: WCSession

    init(session: WCSession = .default) {
        self.session = session
        super.init()
        self.session.delegate = self
        self.connect()
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

视图模型:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

场景委托:

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

...

window.rootViewController = UIHostingController(rootView: contentView)
Run Code Online (Sandbox Code Playgroud)

  • 你有使用 swiftUI 的工作代码吗?我一直很难让手表接收来自手机的消息。我可以使用苹果提供的 SimpleWatchConnectivity Project,但它使用故事板。如果能有一个 SwiftUI 的基本起点就好了。我感觉这里少了点什么,不是要给手表设置接收功能吗?在上面的代码中,没有指示如何处理手表扩展。 (2认同)