In my project, I use Watch Connectivity
to send messages to and from the Watch and iPhone. I can send a message to the phone and receive an array of strings when launching the app, however when using actions I get the following error;
Error Domain=WCErrorDomain Code=7012 "Message reply took too long."
Here's how things are set up;
First, the watch sends a message to the phone and then the phone sends an array of strings to display in a WKInterfaceTable
. This sometimes works when loading the app. ( I fetch all NSManagedObjects called Items
and use their title
string properties to store in an array
called watchItems
.
However, I have an action on the watch to delete all items in the array and refresh the table with the new data.
The action on the watch uses a sendMessage
function to send the item
to the phone to delete from the array, then the phone sends the newly updated array to the watch and the watch updates the table. However, I either get the same array back or an error.
Pretty simple right, so everything actually worked fine before Swift 3 and Watch OS3/iOS 10; the entire app used to work.
Here's how I have everything set up;
Phone App Delegate
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 ] )
}
}
WATCH
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()
}
}
WCSession
APIs only takes dictionaries with property list types, but I see you are sendingItems
. What are these objects, and are you sure they are supported by the WCSession APIs? – CommingleNSManagedObject
however I fetch them and use their title properties which are of string value to create an array, before sending to the watch. However for each fetch on the watch, the phone is supposed to update the data and refetch. This isn't happening and often get the error. – Mcneely