从函数中检索结果(Swift/WatchOS 2)

从函数中检索结果(Swift/WatchOS 2),swift,function,watchos-2,Swift,Function,Watchos 2,我有一个问题,很可能是一个概念上的问题,但我希望有人能给我指出正确的方向。我正在尝试使用新的WCSession*didReceiveMessage*从我的WatchKit扩展中的iOS应用程序接收数据字典。当我打印收到的消息时,我正确地接收了数据,但我无法在函数之外处理这些数据(例如填充WKInterfaceTable,这是我的最终目标) InterfaceController.swift(WatchOS): class InterfaceController: WKInterfaceContr

我有一个问题,很可能是一个概念上的问题,但我希望有人能给我指出正确的方向。我正在尝试使用新的WCSession*didReceiveMessage*从我的WatchKit扩展中的iOS应用程序接收数据字典。当我打印收到的消息时,我正确地接收了数据,但我无法在函数之外处理这些数据(例如填充WKInterfaceTable,这是我的最终目标)

InterfaceController.swift(WatchOS):

class InterfaceController: WKInterfaceController, WCSessionDelegate {

var session : WCSession!
var files = [String]()

@IBOutlet var fileTable: WKInterfaceTable!

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)

    // Configure interface objects here.
    startSession()
    reloadTable()
}

private func startSession() {
    if (WCSession.isSupported()) {
        session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
        print("activated session")
    }
}

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

func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    let files = message["myFiles"] as? String
    print(files!)
    reloadTable()

}

func reloadTable() {
    print("Test: \(files)")

    fileTable.setNumberOfRows(files.count, withRowType: "FileTableRowController")

    for (index, file) in files.enumerate() {
        if let row = fileTable.rowControllerAtIndex(index) as? FileTableRowController {

            row.fileLabel.setText("test")
        }
    }
}
...
let session = WCSession.defaultSession()
                print("Session is reachable: \(session.reachable)") // this is false
                let msg = ["myFiles":String(self.files)]
                session.sendMessage(msg, replyHandler: { reply in
                    print("Got reply: \(reply)")
                    }, errorHandler: { error in
                        print("error: \(error)")
                })
...
TableViewController.swift(iOS):

class InterfaceController: WKInterfaceController, WCSessionDelegate {

var session : WCSession!
var files = [String]()

@IBOutlet var fileTable: WKInterfaceTable!

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)

    // Configure interface objects here.
    startSession()
    reloadTable()
}

private func startSession() {
    if (WCSession.isSupported()) {
        session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
        print("activated session")
    }
}

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

func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    let files = message["myFiles"] as? String
    print(files!)
    reloadTable()

}

func reloadTable() {
    print("Test: \(files)")

    fileTable.setNumberOfRows(files.count, withRowType: "FileTableRowController")

    for (index, file) in files.enumerate() {
        if let row = fileTable.rowControllerAtIndex(index) as? FileTableRowController {

            row.fileLabel.setText("test")
        }
    }
}
...
let session = WCSession.defaultSession()
                print("Session is reachable: \(session.reachable)") // this is false
                let msg = ["myFiles":String(self.files)]
                session.sendMessage(msg, replyHandler: { reply in
                    print("Got reply: \(reply)")
                    }, errorHandler: { error in
                        print("error: \(error)")
                })
...

似乎在WatchKit接收到
文件并正确打印后,该变量不会在其他地方填充。因此,我无法重新加载表,甚至无法在
reloadTable()
函数中打印。尽管我已尽了最大努力。

这里我可以建议的一件事是,无法通过read table方法访问files变量,请尝试将files变量传递给readtable函数

另外,既然您已经用Let定义了文件(实际上是静态的),那个么重新加载表需要什么呢?因为在这种情况下不会更改文件

您可以尝试在文件表被清除的更高级别上将文件定义为变量


请让我知道这是否有效

我可以在这里建议的一件事是,无法从读取表方法访问文件变量,请尝试将文件变量传递给读取表函数

另外,既然您已经用Let定义了文件(实际上是静态的),那个么重新加载表需要什么呢?因为在这种情况下不会更改文件

您可以尝试在文件表被清除的更高级别上将文件定义为变量


请让我知道这是否有效,WCSessionDelegate方法和块处理程序是在串行后台队列上调用的,因此如果要更新UI,您必须首先调度到主队列

在串行后台队列上调用WCSessionDelegate方法和块处理程序,因此,如果要更新UI,必须首先将其分派到主队列,因为您没有将文件分配给类变量,因此它会在需要之前被释放。重新加载UI时也应使用dispatch_async

我认为这里的主要问题是,您试图将自定义FileData对象作为字典中的字符串发送,但这是通过获取对象的字符串值来实现的,这是行不通的。您需要添加对象的一些自定义序列化,以便能够将其转换为可以在消息负载中发送的字典。下面是如何使用具有两个属性的测试类执行此操作的示例:

internal class Testing: NSObject {

    var name: String
    var someNumber: NSNumber

    init(name: String, someNumber: NSNumber) {
        self.name = name
        self.someNumber = someNumber
    }

    func serialize() -> [String:AnyObject] {
        var transfer = [String:AnyObject]()
        transfer["name"] = self.name
        transfer["someNumber"] = self.someNumber
        return transfer
    }

    class func deserialize(transfer: [String:AnyObject]) -> Testing {
        let deserialized = Testing(
            name: transfer["name"] as! String,
            someNumber:transfer["someNumber"] as! NSNumber)

        return deserialized
    }
}
然后,您可以使用它从iOS控制器发送消息,如下所示:

...
let session = WCSession.defaultSession()
                let testing = Testing(name: "my name", someNumber: 1)
                let serializedTesting = testing.serialize()
                let msg = ["myFiles":serializedTesting]
                session.sendMessage(msg, replyHandler: { reply in
                    print("Got reply: \(reply)")
                    }, errorHandler: { error in
                        print("error: \(error)")
                })
...
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    // Pull out the serialized object
    let files = message["myFiles"] as? [String:AnyObject]

    // Deserialize back to object
    self.files = Testing.deserialize(serializedTesting)

    // Update the UI
    dispatch_async(dispatch_get_main_queue()) {
        reloadTable()
    }
}
您可以从watch OS控制器中读取以下消息:

...
let session = WCSession.defaultSession()
                let testing = Testing(name: "my name", someNumber: 1)
                let serializedTesting = testing.serialize()
                let msg = ["myFiles":serializedTesting]
                session.sendMessage(msg, replyHandler: { reply in
                    print("Got reply: \(reply)")
                    }, errorHandler: { error in
                        print("error: \(error)")
                })
...
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    // Pull out the serialized object
    let files = message["myFiles"] as? [String:AnyObject]

    // Deserialize back to object
    self.files = Testing.deserialize(serializedTesting)

    // Update the UI
    dispatch_async(dispatch_get_main_queue()) {
        reloadTable()
    }
}
更新

关于如何在iOS应用程序和watchOS应用程序之间发送自定义对象,似乎出现了一些混乱。Apple的文档显示,我们需要将对象覆盖到NSdATE中,或者将自定义对象序列化到NScript中,NScript在发送之前只包含基础类型(NSnSnpe、NStNo、NSdEt等)。即使开发人员只有几个相互关联的简单自定义对象,这也会给他们带来很大的开销

为了缓解我自己项目中的这种痛苦,我使用swift 2.0创建了一个通用的序列化器/反序列化器类,该类使用非常简单,并设计用于iOS和watchOS之间的通信

要传输的每个自定义类只需扩展可序列化类并在类的顶部添加@objc标记。作为上述示例,我使用的测试类将变成:

@objc(Testing)
internal class Testing: Serializable {
    var name: String
    var someNumber: NSNumber
}

它在上可用,并包含一个带有工作示例的游乐场。

首先,您没有将文件分配给类变量,因此它会在您希望它释放之前释放。重新加载UI时也应使用dispatch_async

我认为这里的主要问题是,您试图将自定义FileData对象作为字典中的字符串发送,但这是通过获取对象的字符串值来实现的,这是行不通的。您需要添加对象的一些自定义序列化,以便能够将其转换为可以在消息负载中发送的字典。下面是如何使用具有两个属性的测试类执行此操作的示例:

internal class Testing: NSObject {

    var name: String
    var someNumber: NSNumber

    init(name: String, someNumber: NSNumber) {
        self.name = name
        self.someNumber = someNumber
    }

    func serialize() -> [String:AnyObject] {
        var transfer = [String:AnyObject]()
        transfer["name"] = self.name
        transfer["someNumber"] = self.someNumber
        return transfer
    }

    class func deserialize(transfer: [String:AnyObject]) -> Testing {
        let deserialized = Testing(
            name: transfer["name"] as! String,
            someNumber:transfer["someNumber"] as! NSNumber)

        return deserialized
    }
}
然后,您可以使用它从iOS控制器发送消息,如下所示:

...
let session = WCSession.defaultSession()
                let testing = Testing(name: "my name", someNumber: 1)
                let serializedTesting = testing.serialize()
                let msg = ["myFiles":serializedTesting]
                session.sendMessage(msg, replyHandler: { reply in
                    print("Got reply: \(reply)")
                    }, errorHandler: { error in
                        print("error: \(error)")
                })
...
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    // Pull out the serialized object
    let files = message["myFiles"] as? [String:AnyObject]

    // Deserialize back to object
    self.files = Testing.deserialize(serializedTesting)

    // Update the UI
    dispatch_async(dispatch_get_main_queue()) {
        reloadTable()
    }
}
您可以从watch OS控制器中读取以下消息:

...
let session = WCSession.defaultSession()
                let testing = Testing(name: "my name", someNumber: 1)
                let serializedTesting = testing.serialize()
                let msg = ["myFiles":serializedTesting]
                session.sendMessage(msg, replyHandler: { reply in
                    print("Got reply: \(reply)")
                    }, errorHandler: { error in
                        print("error: \(error)")
                })
...
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    // Pull out the serialized object
    let files = message["myFiles"] as? [String:AnyObject]

    // Deserialize back to object
    self.files = Testing.deserialize(serializedTesting)

    // Update the UI
    dispatch_async(dispatch_get_main_queue()) {
        reloadTable()
    }
}
更新

关于如何在iOS应用程序和watchOS应用程序之间发送自定义对象,似乎出现了一些混乱。Apple的文档显示,我们需要将对象覆盖到NSdATE中,或者将自定义对象序列化到NScript中,NScript在发送之前只包含基础类型(NSnSnpe、NStNo、NSdEt等)。即使开发人员只有几个相互关联的简单自定义对象,这也会给他们带来很大的开销

为了缓解我自己项目中的这种痛苦,我使用swift 2.0创建了一个通用的序列化器/反序列化器类,该类使用非常简单,并设计用于iOS和watchOS之间的通信

要传输的每个自定义类只需扩展可序列化类并在类的顶部添加@objc标记。作为上述示例,我使用的测试类将变成:

@objc(Testing)
internal class Testing: Serializable {
    var name: String
    var someNumber: NSNumber
}

它位于一个操场上,包含一个工作示例。

谢谢@ccjensen,总是很有帮助的!我确实尝试在WCsession函数中添加
dispatch\u async(dispatch\u get\u main\u queue(),{()->Void in self.reloadTable())}
,但它似乎仍然没有将“files”字典传递到表本身。也许我做错了?@Lehn0058似乎是对的。除了调度之外,您还需要停止