iPhone 未从 Apple Watch 接收应用程序上下文

iPhone not receiving applicationContext from AppleWatch

我创建了一个程序来测试从 iPhone 和 AppleWatch 来回发送数据,反之亦然。我已将其设置为在 AppleWatch 上有一个按钮,在 iPhone 上有一个按钮。当 iPhone 按下时,它将发送数据并将 AppleWatch 上的按钮重命名为该数据字符串。

然后我为 AppleWatch 实现了与 iPhone 相同的代码,但由于某种原因 iPhone 似乎没有收到数据。这是 iPhone 的代码:

//  ViewController.swift

import UIKit
import Foundation
import WatchConnectivity

class WatchManager: UIViewController, WCSessionDelegate {

    var watchSession: WCSession? {
        didSet {
            if let session = watchSession {
                session.delegate = self
                session.activate()
            }
        }
    }

    override func viewDidLoad(){
        super.viewDidLoad()

        watchSession = WCSession.default

    }

    private func sendDict(_ dict: [String: Any]) {
        do {
            try self.watchSession?.updateApplicationContext(dict)
        } catch {
            print("Error sending dictionary \(dict) to Apple Watch!")
        }
    }

    @IBOutlet weak var transferButton: UIButton!

    @IBAction func dataTransfer(_ sender: Any) {
        sendDict(["DataKey": UUID().uuidString])
        print("sent")
    }

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("Session activation did complete")
    }

    public func sessionDidBecomeInactive(_ session: WCSession) {
        print("session did become inactive")
    }

    public func sessionDidDeactivate(_ session: WCSession) {
        print("session did deactivate")
    }

    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        print("phone received app context: ", applicationContext)
        if let temperature = applicationContext["DataKey"] as? String {
            self.transferButton.setTitle(temperature, for: .normal)
        }

    }

}

和 AppleWatch:

//  InterfaceController.swift

import WatchKit
import Foundation
import WatchConnectivity


class InterfaceController: WKInterfaceController {

    var watchSession: WCSession? {
        didSet {
            if let session = watchSession {
                session.delegate = self
                session.activate()
            }
        }
    }


    @IBOutlet weak var temperatureLabel: WKInterfaceButton!

    private func sendDict(_ dict: [String: Any]) {
        do {
            try self.watchSession?.updateApplicationContext(dict)
        } catch {
            print("Error sending dictionary \(dict) to iPhone!")
        }
    }

    @IBAction func button() {
        let urg = ["DataKey":UUID().uuidString]
        sendDict(urg)
        print("watch sent app context \(urg)")
    }

}

extension InterfaceController: WCSessionDelegate {

    #if os(iOS)
    public func sessionDidBecomeInactive(_ session: WCSession) { }
    public func sessionDidDeactivate(_ session: WCSession) {
        session.activate()
    }
    #endif

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("Session activation did complete")
    }

    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        print("watch received app context: ", applicationContext)
        if let temperature = applicationContext["DataKey"] as? String {
            self.temperatureLabel.setTitle(temperature)
        }

    }

    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()

        watchSession = WCSession.default

    }

    override func didDeactivate() {
        // This method is called when watch view controller is no longer visible
        super.didDeactivate()
    }

}

我试过更改密钥的名称(没有用),我做到了,所以数据值总是在变化 (UUID().uuidString) 并保留了它。我尝试的其他事情包括创建一个标签并尝试重命名它而不是失败的按钮,最后而不是重命名按钮只是发送一些确认它收到数据返回到失败的苹果手表。

非常感谢任何帮助,我希望这不是一个愚蠢的错误。

我认为 updateApplicationContext 不是满足您需求的正确方法。 从文档: 系统会在机会出现时发送上下文数据,目标是在对方醒来时准备好数据。 如果你想在两个应用程序都在前台时来回发送数据 sendMessage 应该可以。

最终您可能需要结合使用这两种方法。我建议阅读以下文档:https://developer.apple.com/documentation/watchconnectivity/wcsession

编辑:

为了让 "In the end you may need to implement a combination of both methods" 的意思更清楚,我从我的一个应用程序中添加了一些示例代码。

方法 _sendData 尝试通过 sendMessageData 发送当前数据,前提是手表可以访问。如果不是,它会更新应用程序上下文,使数据在手表应用程序启动时可用。

- (void)_sendCurrentData
{
    if ([WCSession defaultSession].isPaired && [WCSession defaultSession].isWatchAppInstalled)
    {
        if ([WCSession defaultSession].isReachable)
        {
            // Send data to watch
            [[WCSession defaultSession] sendMessageData:self.currentData
                                           replyHandler:nil
                                           errorHandler:^(NSError * _Nonnull error) {
                                               [self _updateApplicationContext];
                                           }];
        }
        else
        {
            [self _updateApplicationContext];
        }
    }
}

- (void)_updateApplicationContext
{
    if ([WCSession defaultSession].isPaired && [WCSession defaultSession].isWatchAppInstalled)
    {
        NSError* error = nil;
        [[WCSession defaultSession] updateApplicationContext:@{@"data": self.currentData}
                                                       error:&error];
        if (error != nil)
        {
            NSLog(@"error while updating application context: %@", error.localizedDescription);
        }
    }
}
use self.watchSession?.transferUserInfo(dict) instead of self.watchSession?.updateApplicationContext(dict) 
you'll got a call back in:
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
}