39

在我的项目中,我使用Watch Connectivity在 Watch 和 iPhone 之间发送消息。我可以在启动应用程序时向手机发送消息并接收一系列字符串,但是在使用操作时出现以下错误;

错误域 = WCErrorDomain 代码 = 7012 “消息回复时间过长。”

这是设置的方式;

首先,手表向手机发送一条消息,然后手机发送一个字符串数组以显示在WKInterfaceTable. 这有时在加载应用程序时有效。(我获取所有调用的 NSManagedObjectsItems并使用它们的title字符串属性存储在array调用的watchItems.

但是,我在手表上有一个操作来删除数组中的所有项目并用新数据刷新表。

手表上的动作使用一个sendMessage函数发送item到手机从数组中删除,然后手机将新更新的数组发送到手表,手表更新表。但是,我要么得到相同的数组,要么得到一个错误。

很简单,所以在 Swift 3 和 Watch OS3/iOS 10 之前一切正常;整个应用程序曾经工作。

这是我设置所有内容的方式;

电话应用程序代表

import WatchConnectivity

class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate {

var session : WCSession!

var items = [Items]()

func loadData() {
    let moc = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
    let request = NSFetchRequest<Items>(entityName: "Items")

    request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
    request.predicate = NSPredicate(format: "remove == 0", "remove")

    do {
        try
            self.items = moc!.fetch(request)
        // success ...
    } catch {
        // failure
        print("Fetch failed")
    }
}

//WATCH EXTENSION FUNCTIONS
//IOS 9.3 
/** Called when the session has completed activation. If session state is WCSessionActivationStateNotActivated there will be an error with more details. */


//HAVE TO INCLUDE
@available(iOS 9.3, *)
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?){
   print("iPhone WCSession activation did complete")
}


@available(iOS 9.3, *)
func sessionDidDeactivate(_ session: WCSession) {}

func sessionWatchStateDidChange(_ session: WCSession) {}

func sessionDidBecomeInactive(_ session: WCSession) {

}

//APP DELEGATE FUNCTIONS

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

    //Check if session is supported and Activate
    if (WCSession.isSupported()) {
        session = WCSession.default()
        session.delegate = self;
        session.activate()
    }
    return true
}


}

//DID RECIEVE MESSAGE
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void) {


    loadData()

    func loadItems() {
        watchItems.removeAll()

        for a in self.items {
            watchItems.append(a.title)
        }
    }

    var watchItems = ["1","2","3","4","5"]

    let value = message["Value"] as? String

    //This is called when user loads app, and takes some time when using refresh action, sometimes times out 

    if value == "HELLOiPhone/+@=" {

        print("Hello Message Recieved")

        loadItems() 

        //send a reply
        replyHandler( [ "Items" : Items ] )

    }

    //Not sure if receiving but does not delete array and send back to watch
    if value == "removeALL@+=-/" {                        
        for index in self.items {
            index.remove = 1
            //Saves MOC
        }

        loadData()
        loadTasksData()

        //send a reply
        replyHandler( [ "Items" : Items ] )

    }
    else {
        for index in self.items {
            if index.title == value {
            index.remove = 1
            //Saves MOC
            }
        }

        loadData()
        loadTasksData()

        //send a reply
        replyHandler( [ "Items" : Items ] )
    }
}

手表

import WatchConnectivity

class SimplelistInterfaceController: WKInterfaceController, WCSessionDelegate  {


/** Called when the session has completed activation. If session state is WCSessionActivationStateNotActivated there will be an error with more details. */
@available(watchOS 2.2, *)
public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {

   //Fetch data is a function which sends a "HELLOiPhone/+@=" message to receive the array and displays in the table. This works 
   fetchData()
}


var session : WCSession!
var items = ["Refresh Items"]

override func didAppear() {
    fetchData()
}

override func willActivate() {
    // This method is called when watch view controller is about to be visible to user
    super.willActivate()
    //Check if session is supported and Activate
    if (WCSession.isSupported()) {
        session = WCSession.default()
        session.delegate = self
        session.activate()
    }
    fetchData()
}

override func awake(withContext context: Any?) {
    super.awake(withContext: context)
    fetchData()
}

@IBAction func refresh() {
    print("Refresh")
    //Works but sometimes message is delayed
    fetchData()
}

@IBAction func removeAll() {
    print("Remove All Items is called")
    if WCSession.default().isReachable {
        let messageToSend = ["Value":"removeALL@+=-/"]
        print("\(messageToSend)")
        session.sendMessage(messageToSend, replyHandler: { replyMessage in
            if let value = replyMessage["Items"] {
                self.items = value as! [String]

                Not receiving message
                print("Did Recieve Message, items = \(self.items)")
            }

            }, errorHandler: {error in
                // catch any errors here
                print(error)
        })
    }
    fetchData()
}

}
4

2 回答 2

2
  1. 您不应将自定义类对象从一个目标(iOS)发送或接收到第二个目标(watchOS),而应以字典格式发送/接收数据,例如 [String: Any],并且此字典应包含自定义对象所需属性的数组在简单字典中的键值对中。这很容易在手表端解码。

  2. 您应该像下面那样创建一个扩展 WCSessionDelegate 的解耦类,以便该类不仅可以在 ExtensionDelegate 中使用,而且可以在任何 WKInterfaceController 中使用。

    class WatchSessionManager: NSObject, WCSessionDelegate {
    
        static let sharedManager = WatchSessionManager()
    
        private override init() {
            super.init()
            self.startSession()
        }
    
        private let session: WCSession = WCSession.default
    
        func startSession() {
            session.delegate = self
            session.activate()
        }
    
        func tryWatchSendMessage(message: [String: Any], completion: (([String: Any]) -> Void)? = nil) {
            print("tryWatch \(message)")
            weak var weakSelf = self
            if #available(iOS 9.3, *) {
                if weakSelf?.session.activationState == .activated {
                    if weakSelf?.session.isReachable == true {
                        weakSelf?.session.sendMessage(message,
                                                      replyHandler: { [weak self]  ( response )  in
                                                        guard let slf = self else {return}
                                                        //Get the objects from response dictionary
                                                        completion?(response)
                            },
                                                      errorHandler: { [weak self] ( error )  in
                                                        guard let slf = self else {return}
                                                        print ( "Error sending message: % @ " ,  error )
                                                        // If the message failed to send, queue it up for future transfer
                                                        slf.session.transferUserInfo(message)
                        })
                    } else {
                        self.session.transferUserInfo(message)
                    }
                }else{
                    self.session.activate()
                    self.session.transferUserInfo(message)
                }
            } else {
                // Fallback on earlier versions
                if self.session.activationState == .activated {
                    if self.session.isReachable == true {
                        self.session.sendMessage(message,
                                                 replyHandler: {  ( response )  in
                                                    //Get the objects from response dictionary
                                                    completion?(response)
                        },
                                                 errorHandler: {  ( error )  in
                                                    print ( "Error sending message: % @ " ,  error )
                                                    // If the message failed to send, queue it up for future transfer
                                                    self.session.transferUserInfo(message)
                        })
                    } else {
                        self.session.transferUserInfo(message)
                    }
                }else{
                    self.session.activate()
                    self.session.transferUserInfo(message)
                }
            }
        }
    
    
    }
    

现在,您可以使用任何 WKInterfaceController 中的上述函数轻松地向您的 iOS 应用程序发送一条消息以唤醒并从那里获取数据(例如从 CoreData),并且完成块将具有您所需的数据,例如

let dict: [String: Any] = ["request": "FirstLoad"]
WatchSessionManager.sharedManager.tryWatchSendMessage(message: dict,completion:{ (data) in print(data)})

同样,您应该在 iOS 端使用此 WatchSessionManager 并接收请求,根据请求的键,您应该从核心存储/数据库获取数据,并在 didreceiveMessage 函数的 replyHandler 中以简单的键值字典模式发送自定义对象列表,如下所示.

 func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) {
 var dict: [String: Any] = [String: Any]()
 replyHandler(dict) //This dict will contain your resultant array to be sent to watchApp.
}

有时,WatchApp 无法访问 iOS App(Killed 状态),为了解决该问题,您应该在大约 3 秒间隔的 Timer 内调用“tryWatchSendMessage”。当您从 watchApp 获得连接时,您应该使计时器无效。

WatchConnectivity 的 sendMessage 功能非常强大,可以唤醒您的应用程序。您应该以优化的方式使用它。

于 2018-04-01T17:11:52.500 回答
0

我刚刚处理了我的 WatchOS 应用程序。还有一种情况是我收到“消息回复时间太长”。

然后我将后台任务处理程序添加到我的 iOS 应用程序并开始每秒向 WatchOS 应用程序发送消息。该消息包含UIApplication.shared.backgroundTimeRemaining。所以我得到:45 秒,44 秒,...,6 秒,5 秒,...如果计时器运行时间低于 5 秒,则不会向 iOS 应用程序发送消息/从 iOS 应用程序发送消息,我们将收到“消息回复时间过长”。最简单的解决方案是每次计时器低于 15 秒时从手表向手机发送空白消息。将backgroundTimeRemaining再次更新为 45 秒:45, 44, 43, ..., 17, 16, 15, (空白消息), 45, 44, 43, ...

希望它可以帮助某人

于 2016-10-12T16:43:34.880 回答