简体   繁体   中英

Why isn't iPhone transferring an array to Apple Watch?

I have an array stored on my iPhone that I need sent to the Watch in the background whenever it is updated on the phone. I've followed a tutorial on RayWenderlich.com but I just can't seem to get it to work.

Phone code:

import UIKit
import Foundation
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {

var array1 = ["Hi", "This", "Is", "A", "Test"]()
var array2 = ["1", "2", "3", "4", "5"]()

 var session: WCSession?

private func startSession() {

    if WCSession.isSupported() {

        session = WCSession.defaultSession()
        session?.delegate = self
        session?.activateSession()

    }

}
private func sendToWatch() {

    if let session = session where session.reachable {

        session.transferUserInfo(["Array1": array1])

    }

    if let session = session where session.reachable {

        session.transferUserInfo(["Array2": array2])

    }

}

sendToWatch()

}

And Watch code:

import WatchKit
import Foundation
import WatchConnectivity

var array1 = [String]()

var array2 = [String]()

class InterfaceController: WKInterfaceController, WCSessionDelegate {

var session: WCSession?

@IBOutlet var table: WKInterfaceTable!

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

    startSession()

    table!.setNumberOfRows(array1.count, withRowType: "tableRowController")

    var index = 0

    while index < array1.count {

        let row = table.rowControllerAtIndex(index) as! tableRowController

        row.rowLabel.setText(array1[index])

        row.dateLabel.setText(array2[index])

        index++

    }

}

private func startSession() {

    if WCSession.isSupported() {

        session = WCSession.defaultSession()
        session?.delegate = self
        session?.activateSession()

    }

}

func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {

    if let received1 = userInfo["Array1"] as? [String] {
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            array1 = received1
        })
    }

    if let received2 = userInfo["Array2"] as? [String] {
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            array2 = received2
        })
    }

}

}

The Table on the watch works fine if I enter dummy data into the arrays, so I know everything is set up correctly. I'm really only having problems with the Transfer/Receiving.

SECOND ATTEMPT:

I'm now trying to use updateApplicationContext() as opposed to transferUserInfo() . I've removed the session.reachable check and changed some code.

Phone code:

 private func sendToWatch() {
do {
        let applicationDict = ["Array1": array1]
        let applicationDict2 = ["Array2": array2]
        try WCSession.defaultSession().updateApplicationContext(applicationDict)
        try WCSession.defaultSession().updateApplicationContext(applicationDict2)
    }

catch {
        print(error)
    }
}

Watch code:

func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {

    //I can't figure out how to retrieve the data!

    print(applicationContext) // returns nil 
    array1 = applicationContext["Array1"] as! [String] //returns nil and crashes app
    array2 = applicationContext["Array2"] as! [String] //returns nil and crashes app

}

THIRD ATTEMPT:

I'm now able to sometimes get either array1 or array2 to appear on the Watch. Of course, it crashes the app as both arrays are not successfully received. Can anybody help me with this?

Watch Code:

func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {

    dispatch_async(dispatch_get_main_queue()) { () -> Void in

        if let retrievedArray1 = applicationContext["Array1"] as? [String] {

            array1 = retrievedArray1

            print(array1)

        }

        if let retrievedArray2 = applicationContext["Array2"] as? [String] {

            array2 = retrievedArray2

            print(array2)

        }

        self.table!.setNumberOfRows(array1.count, withRowType: "tableRowController")

        var index = 0

        while index < array1.count {

            let row = self.table.rowControllerAtIndex(index) as! tableRowController

            row.rowLabel.setText(array1[index]) //My crash happens either here or....

            row.dateLabel.setText(array2[index])  //here because both arrays aren't being received and unreceived array is out of index

            index++

        }

    }

}

With respect to your third try:

You don't want to use updateApplicationContext - you want to use transferUserInfo, as elsewhere suggested.

Each updateApplicationContext call will overwrite any preceding data that have have been sent to the watch. When you send dict2 immediately after the dict1, the first dictionary is discarded and replaced with dict2. You cannot get both dictionaries in this way via updateApplicationContext.

You would use updateApplicationContext to pass, for example, settings or options in your app where only the most recent setting is important. For example, if you have a flag that could be changed in the iOS app, you would use updateApplicationContext every time the flag was changed in the iOS app. When the watch app eventually is loaded, it will only see the last setting of the flag in didReceiveApplicationContext.

The way your third try is set up now, the first dict is sent and then immediately followed by the second dict. You are seeing intermittent failures based on variable lag times in the delivery of those two dictionaries.

If the didReceiveApplicationContext is invoked before the second dictionary is completely transferred, you will successfully retrieve dictionary 1 since that data hasn't been overwritten yet. It will fail when it tries to access index "Array 2" because it is not there yet. If the second context is transferred before the watch hits the retrieve "Array 1" call, then your app will fail there because the last data received is not keyed on "Array1", but rather "Array2".

Calls using updateUserInfo do not overwrite preceding calls. When the watch app is loaded, it will trigger the didReceiveUserInfo code once for each call. The order of the calls is maintained, so if you sent dict1, dict2, and then dict3 from the iOS app, the watch app will get them in that order.

session.reachable will only be true when the Watch app is in the foreground (there are a few rare exceptions).

If you want to send data to the watch app in the background, you should be using updateApplicationContext , transferUserInfo or transferFile (which one you choose depends on what you are sending and its size).

So something like:

Updated code in response to 3rd edit/question by originator

Phone code:

private func sendToWatch() {
do {
        let applicationDict = ["Array1": array1, "Array2": array2]
        try WCSession.defaultSession().updateApplicationContext(applicationDict)
    }

catch {
        print(error)
    }
}

Watch code:

func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {

    dispatch_async(dispatch_get_main_queue()) { () -> Void in

        if let retrievedArray1 = applicationContext["Array1"] as? [String] {

            array1 = retrievedArray1

            print(array1)

        }

        if let retrievedArray2 = applicationContext["Array2"] as? [String] {

            array2 = retrievedArray2

            print(array2)

        }

        self.table!.setNumberOfRows(array1.count, withRowType: "tableRowController")

        var index = 0

        while index < array1.count {

            let row = self.table.rowControllerAtIndex(index) as! tableRowController

            row.rowLabel.setText(array1[index])

            row.dateLabel.setText(array2[index])

            index++

        }

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM