How can I send a data back to iPhone using WatchConnectivity - ios

I have a Habits app with several counters. Now I figure out how to send a counter's value to iWatch. The question is: when I push a button and change a counter's value on iWatch (for example +1) how can I send the data back to iPhone?
To send a count value from iPhone I have a code in ViewController:
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { }
func sessionDidBecomeInactive(_ session: WCSession) { }
func sessionDidDeactivate(_ session: WCSession) { }
var session: WCSession?
var count = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if WCSession.isSupported() {
session = WCSession.default
session?.delegate = self
session?.activate()
}
}
#IBAction func addPlusOne(_ sender: UIButton) {
let count += 1
let sendHabit = String(count)
if let validSession = session {
let iPhoneAppCount = ["Habits": sendHabit]
do {
try validSession.updateApplicationContext(iPhoneAppCount)
} catch {
print("Something went wrong")
}
}
To receive the count value to iWatch I have code In InterfaceController:
let session = WCSession.default
override func awake(withContext context: Any?) {
super.awake(withContext: context)
processApplicationContext()
processApplicationCount()
session.delegate = self
session.activate()
}
func processApplicationCount() {
if let iPhoneContext = session.receivedApplicationContext as? [String : String] {
displayLabel.setText(iPhoneContext["Habits"])
}
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
DispatchQueue.main.async() {
self.processApplicationCount()
}
}

Transferring data from Apple Watch to iPhone is very similar to vice versa.
For global variables you can use updateApplicationContext() of WCSession from the Watch:
let session = WCSession.default()
if session.activationState == .activated {
session.updateApplicationContext(["my_global": g_myGlobal])
}
Then on the phone you should assign a delegate to the WCSession and implement the following:
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
let receivedGlobal = applicationContext["my_global"] as? TypeOfTheGlobal
}

Related

WCSession activated successfully but session.isReachabilty always return false

******** Session Activated ******
Watch is paired true
Watch is reachable false
Watch app installed false
I have established the session but not able to make connection reachability between the iOS app (UIKit) and watchOS app (swiftUI)
This is my watchConnectivity class
import WatchConnectivity
class WatchSessionManager: NSObject, WCSessionDelegate {
static let sharedManager = WatchSessionManager()
public var watchDelegate: WCSessionDelegate?
private override init() {
super.init()
}
private let session: WCSession? = WCSession.isSupported() ? WCSession.default : nil
#available(iOS 9.3, *)
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
switch activationState {
case .activated:
print("WCSession activated successfully")
case .inactive:
print("Unable to activate the WCSession. Error: \(error?.localizedDescription ?? "--")")
case .notActivated:
print("Unexpected .notActivated state received after trying to activate the WCSession")
#unknown default:
print("Unexpected state received after trying to activate the WCSession")
}
// Handle activation completion
print("jew")
if session.isReachable {
let message = ["message": "Hello from iPhone"]
session.sendMessage(message, replyHandler: nil) { (error) in
print(error)
}
}else{
print("Not Reachable")
}
#if os(iOS)
print("******** Session Activated ******")
print("Watch is paired \(session.isPaired)")
print("Watch is reachable \(session.isReachable)")
print("Watch app installed \(session.isWatchAppInstalled)")
#endif
//print("Watch is reachable \(session.isReachable)")
}
#if os(iOS)
func sessionDidBecomeInactive(_ session: WCSession) {
print("Session Inactive")
}
func sessionDidDeactivate(_ session: WCSession) {
print("Session deactivated")
}
#endif
var validSession: WCSession? {
// paired - the user has to have their device paired to the watch
// watchAppInstalled - the user must have your watch app installed
// Note: if the device is paired, but your watch app is not installed
// consider prompting the user to install it for a better experience
#if os(iOS)
if let session = session, session.isPaired && session.isWatchAppInstalled {
print("Watch is paired \(session.isPaired)")
print("Watch app installed \(session.isWatchAppInstalled)")
return session
}
#elseif os(watchOS)
print("Watch is reachable \(session!.isReachable)")
return session
#endif
return nil
}
func startSession() {
session?.delegate = self
session?.activate()
}
}
// MARK: Application Context
// use when your app needs only the latest information
// if the data was not sent, it will be replaced
extension WatchSessionManager {
// Sender
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
guard let content = message["content"] as? String else { return }
DispatchQueue.main.async {
//self.messageLabel.text = content
print(content)
}
}
func updateApplicationContext(applicationContext: [String : AnyObject]) throws {
if let session = validSession {
do {
print("WCSession contect sent \(applicationContext)")
try session.updateApplicationContext(applicationContext)
print("WCSession contect sent \(applicationContext)")
} catch let error {
throw error
}
}else{
print("Error sending context")
}
}
// Receiver
// func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
// // handle receiving application context
// let isEnabledStatus = applicationContext["isEnabled"] as? Bool
//
// // DispatchQueue.main.async {
// print("Received application context \(applicationContext) 1")
// #if os(watchOS)
// if watchDelegate != nil{
// print("Received application context \(applicationContext) 2")
//
// }
// #endif
// // make sure to put on the main queue to update UI!
// // }
// }
}
// MARK: User Info
// use when your app needs all the data
// FIFO queue
extension WatchSessionManager {
// Sender
func transferUserInfo(userInfo: [String : AnyObject]) -> WCSessionUserInfoTransfer? {
return validSession?.transferUserInfo(userInfo)
}
func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?) {
// implement this on the sender if you need to confirm that
// the user info did in fact transfer
}
// Receiver
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
// handle receiving user info
DispatchQueue.main.async {
// make sure to put on the main queue to update UI!
}
}
}
// MARK: Transfer File
extension WatchSessionManager {
// Sender
func transferFile(file: NSURL, metadata: [String : AnyObject]) -> WCSessionFileTransfer? {
return validSession?.transferFile(file as URL, metadata: metadata)
}
func session(_ session: WCSession, didFinish fileTransfer: WCSessionFileTransfer, error: Error?) {
// handle filed transfer completion
}
// Receiver
func session(_ session: WCSession, didReceive file: WCSessionFile) {
// handle receiving file
DispatchQueue.main.async {
// make sure to put on the main queue to update UI!
}
}
}
// MARK: Interactive Messaging
extension WatchSessionManager {
// Live messaging! App has to be reachable
private var validReachableSession: WCSession? {
if let session = validSession , session.isReachable {
return session
}
return nil
}
// Sender
func sendMessage(message: [String : AnyObject],
replyHandler: (([String : Any]) -> Void)? = nil,
errorHandler: ((Error) -> Void)? = nil)
{
validReachableSession?.sendMessage(message, replyHandler: replyHandler, errorHandler: errorHandler)
}
func sendMessageData(data: Data,
replyHandler: ((Data) -> Void)? = nil,
errorHandler: ((Error) -> Void)? = nil)
{
validReachableSession?.sendMessageData(data, replyHandler: replyHandler, errorHandler: errorHandler)
}
// Receiver
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
// handle receiving message
DispatchQueue.main.async {
// make sure to put on the main queue to update UI!
print("Message Received\(message)")
}
}
func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
// handle receiving message data
DispatchQueue.main.async {
// make sure to put on the main queue to update UI!
}
}
}
in ios setting in appdelegate:
WatchSessionManager.sharedManager.startSession() in didFinishLaunchingWithOptions
in watchOS
.onAppear {
WatchSessionManager.sharedManager.startSession()
}
I tried to pass the data between my iOS app and watchOS app
private func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : String]) -> Void) {
replyHandler(["message": "Hello Watch!"])
}

Apple Watch Companion App: sendMessage doesn't work with quit iOS App

I'm currently building an Apple Watch Companion App with Swift and Flutter. I'm doing this with the help of theamorn's Github project. Everything works in the simulator (iOS 15.0 and WatchOS 8.0), even if the iOS App is force quit. However, when testing on my AW Series 3 (WatchOS 8.0) and iPhone 11 (iOS 15.0) it will only work, as long as the iOS App is opened.
My AppDelegate.swift of the iOS App
import UIKit
import Flutter
import WatchConnectivity
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
var session: WCSession?
let methodChannelName: String = "app.controller.watch"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
initFlutterChannel()
if WCSession.isSupported() {
session = WCSession.default;
session!.delegate = self;
session!.activate();
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func initFlutterChannel() {
if let controller = window?.rootViewController as? FlutterViewController {
let channel = FlutterMethodChannel(
name: methodChannelName,
binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler({ [weak self] (
call: FlutterMethodCall,
result: #escaping FlutterResult) -> Void in
switch call.method {
case "flutterToWatch":
guard let watchSession = self?.session, watchSession.isPaired,
watchSession.isReachable, let methodData = call.arguments as? [String: Any],
let method = methodData["method"], let data = methodData["data"] as? Any else {
result(false)
return
}
let watchData: [String: Any] = ["method": method, "data": data]
watchSession.sendMessage(watchData, replyHandler: nil, errorHandler: nil)
result(true)
default:
result(FlutterMethodNotImplemented)
}
})
}
}
}
extension AppDelegate: WCSessionDelegate {
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
}
func sessionReachabilityDidChange(_ session: WCSession) {
print("Watch reachability: \(session.isReachable)")
if (session.isReachable) {
//invoke sendWakeupToFlutter via MethodChannel when reachability is true
DispatchQueue.main.async {
if let controller = self.window?.rootViewController as? FlutterViewController {
let channel = FlutterMethodChannel(
name: self.methodChannelName,
binaryMessenger: controller.binaryMessenger)
channel.invokeMethod("sendWakeupToFlutter", arguments: [])
}
}
}
}
func sessionDidBecomeInactive(_ session: WCSession) {
}
func sessionDidDeactivate(_ session: WCSession) {
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
DispatchQueue.main.async {
if let method = message["method"] as? String, let controller = self.window?.rootViewController as? FlutterViewController {
let channel = FlutterMethodChannel(
name: self.methodChannelName,
binaryMessenger: controller.binaryMessenger)
channel.invokeMethod(method, arguments: message)
}
}
}
}
My WatchViewModel.swift in my Watch Extension
import Foundation
import WatchConnectivity
class WatchViewModel: NSObject, ObservableObject {
var session: WCSession
var deviceList: String = ""
#Published var loading: Bool = false
#Published var pubDeviceList: [Device]?
// Add more cases if you have more receive method
enum WatchReceiveMethod: String {
case sendLoadingStateToNative
case sendSSEDeviceListToNative
}
// Add more cases if you have more sending method
enum WatchSendMethod: String {
case sendWakeupToFlutter
case sendCloseToFlutter
}
init(session: WCSession = .default) {
self.session = session
super.init()
self.session.delegate = self
session.activate()
}
func sendDataMessage(for method: WatchSendMethod, data: [String: Any] = [:]) {
sendMessage(for: method.rawValue, data: data)
}
}
extension WatchViewModel: WCSessionDelegate {
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
}
func sessionReachabilityDidChange(_ session: WCSession) {
print("iPhone reachability: \(session.isReachable)")
if(session.isReachable) {
//invoke sendWakeupToFlutter via sendMessage when reachability is true
sendDataMessage(for: .sendWakeupToFlutter)
}
}
// Receive message From AppDelegate.swift that send from iOS devices
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
DispatchQueue.main.sync {
guard let method = message["method"] as? String, let enumMethod = WatchReceiveMethod(rawValue: method) else {
return
}
switch enumMethod {
case .sendLoadingStateToNative:
self.loading = (message["data"] as? Bool) ?? false
case .sendSSEDeviceListToNative:
self.deviceList = (message["data"] as? String) ?? ""
let data = self.deviceList.data(using: .utf8)!
do {
self.pubDeviceList = try JSONDecoder().decode([Device].self, from: data)
} catch let error {
print(error)
}
}
}
}
func sendMessage(for method: String, data: [String: Any] = [:]) {
guard session.isReachable else {
print("ios not reachable")
return
}
print("ios is reachable")
let messageData: [String: Any] = ["method": method, "data": data]
let callDepth = 10
session.sendMessage(messageData, replyHandler: nil, errorHandler: nil)
}
}
Does someone know how to fix this? Thanks in advance!
EDIT:
The change in my watch extension so far:
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if (activationState == WCSessionActivationState.activated) {
sendDataMessage(for: .sendWakeupToFlutter)
}
}
NOTE: The accepted answer didn't solve my problem entirely, but improved the situation. I ended up making a rather independent Watch App w/o using Flutter MethodChannels.
It probably won't fix your issue but you should be careful about using session.isReachable - its value is only valid for a session that is activated, which it almost certainly is (activate session is async but quick I think), but WatchOS has a number of APIs where the value can only be trusted if certain conditions are met, and you should check, otherwise you end up with a value like true or false when the real value should be 'don't know'
IIRC isReachable is normally true from watch to counterpart iPhone app, you should consider sending the wake up proactively when the session activates.
tl;dr It should probably work when the quit app was built for release.
Long Version
I had the same issue. What I found out is that it works fine when building the flutter app in release mode (rather than just running it via XCode/non release mode).
The reason for that is that, when opening a quit app that wasn't built for release, you'll see the screen that you need to run it via a Flutter IDE or XCode. Which indicates that the app is not properly launched when trying to launch a non-release app via the home screen. That is why the message that is being sent from the watch will probably not reach the iOS WCSession or make it through the platform channel.

How do I transfer a dictionary with transferUserInfo to Apple Watch?

I am trying to put a part of my Apple Watch app behind a paywall. For that, the iOS app automatically creates a dictionary with a true/false value, whether the content is purchases of not. The problem is, is no matter how I try, I cannot pass it to the Watch.
Here is my iOS ViewController:
import WatchConnectivity
class ViewController: UIViewController, WCSessionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
//The dictionary to be passed to the Watch
var dictionaryToPass = ["product1": 0, "product2": 0]
//This will run, if the connection is successfully completed.
//BUG: After '.activate()'-ing the session, this function successfully runs in the '.activated' state.
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
print("WCSession - activationDidCompleteWith:", activationState, "and error code:", error as Any)
switch activationState {
case .activated:
print("WCSession - activationDidCompleteWith .activated")
//session.transferUserInfo(dictionaryToPass)
case .inactive:
print("WCSession - activationDidCompleteWith .inactive")
case .notActivated:
print("WCSession - activationDidCompleteWith .notActivated")
default:
print("WCSession - activationDidCompleteWith: something other ")
break
}
}
func sessionDidBecomeInactive(_ session: WCSession) {
print("WCSession - sessionDidBecomeInactive")
}
func sessionDidDeactivate(_ session: WCSession) {
print("WCSession - sessionDidDeactivate")
}
//Pushing the button on the iOS storyboard will attempt iOS-watchOS connection.
#IBAction func tuiButton(_ sender: UIButton) {
let session = WCSession.default
if session.isReachable {
session.transferUserInfo(dictionaryToPass)
} else if WCSession.isSupported() {
session.delegate = self
session.activate()
}
}
#IBAction func sendmButton(_ sender: UIButton) {
let session = WCSession.default
if session.isReachable {
session.sendMessage(dictionaryToPass, replyHandler: { reply in
print(reply)
}, errorHandler: nil)
} else if WCSession.isSupported() {
session.delegate = self
session.activate()
}
}
}
And that's what I have on the watchOS's Interface Controller:
import WatchConnectivity
class InterfaceController: WKInterfaceController, WCSessionDelegate {
//The text label on the Watch Storyboard. Helps with debugging.
#IBOutlet weak var helloLabel: WKInterfaceLabel!
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
print("watchOS - activationDidCompleteWith:", activationState)
}
//Whatever arrives, it will get printed to the console as well as the 'helloLabel' will be changed to help the debugging progress.
//BUG: This is the part, that never gets run, even tough the WCSession activated successfully.
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
print("watchOS - didReceiveUserInfo", userInfo)
helloLabel.setText("didReceiveUserInfo")
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
print("watchOS - didReceiveMessage", message)
helloLabel.setText("didReceiveMessage")
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
replyHandler(["does it work?": "yes sir"])
print("watchOS - didReceiveMessage", message)
helloLabel.setText("didReceiveMessage")
}
//Setting the Interface Controller as WCSession Delegate
private var session: WCSession = .default
override func awake(withContext context: Any?) {
session.delegate = self
session.activate()
}
//Activating the session on the watchOS side as well.
override func willActivate() {
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
}
}
Update
After looking at you code I have noticed two main issues:
You are not setting your InterfaceController as a WCSession delegate. The connection needs to be activated from both ends.
class InterfaceController: WKInterfaceController, WCSessionDelegate {
private var session: WCSession = .default
override func awake(withContext context: Any?) {
session.delegate = self
session.activate()
}
}
To be able to receive a message from the counterpart device, you need to implement the session(_:didReceiveMessage:replyHandler:) method. Add these methods to your InterfaceController:
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
print("watchOS - didReceiveMessage", message)
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
replyHandler(["does it work?": "yes sir"])
print("watchOS - didReceiveMessage", message)
}
As you can see, I have also implemented the second funciton that can respond with a replyHandler by calling it a passing some data. This can be useful while debugging.
Update both your button action and a sendMessage call. No need to reactivate a connection it the device is already reachable, also pass a reply handle to make sure watch gives back the data.
#IBAction func button(_ sender: UIButton) {
if session.isReachable {
session.sendMessage(watchInAppPurchases, replyHandler: { reply in
print(reply)
}, errorHandler: nil)
} else if WCSession.isSupported() {
session.delegate = self
session.activate()
}
}
Initial Answer
Do not attempt to sync the data directly after calling activate() since there is no guarantee that the connection is already established. Documentation clearly states that:
This method executes asynchronously and calls the session(_:activationDidCompleteWith:error:) method of your delegate object upon completion.
Since you do set self as a delegate, try to move the transferUserInfo call to the session(_:activationDidCompleteWith:error:) implementation.
func session(
_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?
) {
switch activationState {
case .activated:
session.transferUserInfo(watchInAppPurchases)
default:
// handle other states
break
}
}
Also, when working with Swift make sure to restrain from using CapitalizedCamelCase names for properties, functions etc. Only use this notation for types. I have converted the original WatchInAppPurchases to watchInAppPurchases in the code sample above.
If your call to transferUserInfo still does not work, try to call the sendMessage(_:replyHandler:errorHandler:) instead
switch activationState {
case .activated:
session.sendMessage(watchInAppPurchases, replyHandler: nil, errorHandler: nil)
default:
// handle other states
break
}
and monitor the session(_:didReceiveMessage:replyHandler:) in you watch extension for any incoming messages.
Turns out it was the watchOS simulator that was buggy. Quite a pity one from Apple.
Further reading on Apple's forum: https://developer.apple.com/forums/thread/127460
If anyone else is in the same shoes, I recommend running the code on a physical device, it works perfectly there. The final working code can be found here if anyone from Google results is looking for that.

How to get UserDefaults data to Apple Watch even if iPhone is not active

I need to get a number from userDefaults to use in an Apple Watch app to make some calculations, I'm using the WatchConnectivity framework to get this piece of information but what I don't like about the way I have it right now is that the Phone only sends the data to the Watch when the iPhone app is loaded (viewDidLoad), in other words is I launch the Watch app I need to open the iPhone app in order to get the data to the Apple Watch.
Is it possible to get data from iPhone when it is not active?
Here is the code I'm using:
iOS View Controller
class ViewController: UIViewController, WCSessionDelegate {
var session: WCSession!
override func viewDidLoad() {
super.viewDidLoad()
if WCSession.isSupported() {
session = WCSession.default()
session.delegate = self
session.activate()
}
}
func session(_ session: WCSession, activationDidCompleteWith activationState:WCSessionActivationState, error: Error?) {}
func sessionDidDeactivate(_ session: WCSession) { }
func sessionDidBecomeInactive(_ session: WCSession) { }
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
// Reply to watch with data from user defaults
replyHandler(["dataFromiPhone": readDoubleFromUserDefaults()])
}
}
WatchOS InterfaceController
class InterfaceController: WKInterfaceController, WCSessionDelegate{
var session: WCSession!
var myNumber:Double = 0
override func willActivate() {
super.willActivate()
if (WCSession.isSupported()) {
session = WCSession.default()
session.delegate = self
session.activate()
}
getDataFromiPhone()
}
override func didDeactivate() {
super.didDeactivate()
}
/// Delegate required method
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
func getDataFromiPhone(){
//Send Message to phone - I'm not sure if I need this message
let messageToSend = ["askiPhone":"Hi Phone, send me data from userDefaults."]
session.sendMessage(messageToSend, replyHandler: { replyMessage in
/// handle the reply
let dataFromPhone = replyMessage["dataFromiPhone"] as? String
DispatchQueue.main.async {
self.myNumber = Double(dataFromPhone!)!
}
}, errorHandler: {error in
/// catch any errors here
print("ERROR: \(error)")
})
}
}
I would recommend using App Groups for this. Add an App Group under "Capabilities" for your app's target and the watch extension's target:
And then set your UserDefaults using that App Group:
let appGroupName = "group.mobilemind.SpeedDial"
let appGroupUserDefaults = UserDefaults(suiteName: appGroupName)!
appGroupUserDefaults.set(NSNumber(value: newValue), forKey: "hasRated")
appGroupUserDefaults.synchronize()
Use UserDefaults this way on the app and the watch extension and both will be able to get and set the UserDefaults for the app group.
Edit
This approach does not work on WatchOS 2.0+. However, this approach still works on other types of app extensions.

WCSession communication delayed/unreliable in iOS/watchOS simulator

After upgrading to XCode 8, I had an app update rejected because a button in the watch app wasn't having an effect on the iOS app. Re-running in the simulator, it seemed that sending messages via WCSession was occasionally failing without error on one machine, and almost always on another I use.
I've created a simple app that replicates the behavior, with a button and counter in both the iOS and watch apps. The intention is that tapping the button in the iOS app will increment the counter in the watch app and vice versa.
A few times when I've left the apps running a while, the messages were eventually delivered (after about 2 minutes).
iOS App ViewController:
class ViewController: UIViewController, WCSessionDelegate {
var count: Int = 0
var watchSession: WCSession?
#IBOutlet var countLabel: UILabel?
#IBAction func sendMessage(sender: AnyObject) {
watchSession?.sendMessage(
["message":"increment"],
replyHandler: { (message: [String : Any]) in
},
errorHandler: { (err: Error) in
})
}
override func viewDidLoad() {
super.viewDidLoad()
if(WCSession.isSupported()){
watchSession = WCSession.default()
watchSession!.delegate = self
watchSession!.activate()
}
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
count+=1
updateView()
replyHandler([:])
}
func updateView(){
DispatchQueue.main.async {
self.countLabel?.text = ("\(self.count)")
}
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
func sessionDidBecomeInactive(_ session: WCSession){}
func sessionDidDeactivate(_ session: WCSession){}
}
watchOS InterfaceController:
class InterfaceController: WKInterfaceController, WCSessionDelegate {
var count: Int = 0
var watchSession : WCSession?
#IBOutlet var countLabel: WKInterfaceLabel?
#IBAction func sendMessage(sender: AnyObject) {
watchSession?.sendMessage(
["message":"increment"],
replyHandler: { (message: [String : Any]) in
},
errorHandler: { (err: Error) in
})
}
override func willActivate() {
super.willActivate()
if(WCSession.isSupported()){
watchSession = WCSession.default()
watchSession!.delegate = self
watchSession!.activate()
}
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
count+=1
updateView()
replyHandler([:])
}
func updateView(){
countLabel?.setText("\(self.count)")
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
}

Resources