[Disclaimer: this question is intended to be a wiki question to answer the frequent questions about sending data between the iOS and watch apps under the watchkit
and watch-os
tags.]
I am developing an Apple Watch app and would like to send data between the iOS
app and its WatchKit extension
. I have looked at the WatchConnectivity
framework, but don't really understand the difference between all of its methods.
Which function should I use if I want to be able to send data even when my apps are in the background?
Which function should I use to send UI updates to the Watch?
Which function should I use to send large data?
User Defaults Group You have to enable app groups in both your app's and extension's targets as well as at the developer's portal. Then you are able to use UserDefaults container which can be shared between several apps (or between the app and the extension).
Your Apple Watch uses Wi-Fi and Bluetooth to communicate with your paired iPhone. If you have cellular, your watch can also stay connected through a cellular network. Your watch switches between these intelligently to choose the most power-efficient connection.
This means that when you set up a new iPhone and restore it from backup, your latest Apple Watch data is also restored. * If you've set up an Apple Watch for a family member who doesn't have an iPhone, the watch's data is backed up to iCloud directly.
At the time of writing this answer (watchOS3
is the current stable release and watchOS4
is in beta stage), the only option for direct communication between an iOS
app and its WatchKit extension
is the WatchConnectivity framework. (I said direct, because this Q&A is not concerned with using cloud technologies such as CloudKit
to upload files to the internet from one device and download them on the other one.)
First, let's discuss which function of WCSession
should be used for what purpose. For the code examples, please scroll down.
A quick, one-liner about each function and when to use them before diving deep into details:
updateApplicationContext
: synchronise states between the apps, send data to be displayed on the UI (only use it to send small pieces of data)transferUserInfo
: send a dictionary of data in the backgroundtransferFile
: send a file in the backgroundsendMessage
: send an instant message between at least the watch app is running in foregroundupdateApplicationContext(_:) should be used if you want to synchronise your apps (such as keep the UI updated or send state information, like user logged in, etc.). You can send a dictionary of data. Subsequent calls to this function replace the previously sent dictionary, so the counterpart app only receives the last item sent using updateApplicationContext
. The system tries to call this function at an appropriate time for it to receive data by the time it is needed and meanwhile minimising power usage. For this reason, the function can be called when neither app is running in the foreground, but WCSession
needs to be activated for the transfer to succeed. Frequent calls trying to transfer large amount of data using updateApplicationContext
might fail, so for this usage call transferUserInfo
instead.
transferUserInfo(:) and transferCurrentComplicationUserInfo(:) should be used if you want to send data in the background that needs to be received by the other application. Subsequent calls to this method are queued and all information sent from one app to the other is received. transferCurrentComplicationUserInfo
might be used to send complication-related data to the WatchKit extension
using a high-priority message and waking up the WatchKit app
if needed. However, be aware that this function has a daily limit and once it's exceeded, data is transferred using the transferUserInfo
function.
transferFile(_:metadata:) is similar in implementation and nature to transferUserInfo
, but it accepts a fileURL instead of a dictionary as its input parameter and hence it should be used to send files local to the device to its counterpart. Subsequent calls are queued. Received files must be saved to a new location in the session(_:didReceive:)
method, otherwise they are deleted.
sendMessage(:replyHandler:errorHandler:) and sendMessageData(:replyHandler:errorHandler:) send data immediately to a counterpart app. The counterpart app must be reachable before calling this method. The iOS app is always considered reachable, and calling this method from your Watch app wakes up the iOS app in the background as needed. The Watch app is considered reachable only while it is installed and running. The transfer must be initiated in the foreground. Subsequent calls to this method are queued.
For more information please see App programming guide for watchOS - Sharing Data.
Set up WatchConnectivity
in the iOS
app's AppDelegate
:
import UIKit
import WatchConnectivity
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if WCSession.isSupported() {
let session = WCSession.default()
session.delegate = self
session.activate()
}
return true
}
}
extension AppDelegate: WCSessionDelegate {
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
print("Message received: ",message)
}
//below 3 functions are needed to be able to connect to several Watches
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
func sessionDidDeactivate(_ session: WCSession) {}
func sessionDidBecomeInactive(_ session: WCSession) {}
}
Make your WatchKit
class conform to WCSessionDelegate
:
extension InterfaceController: WCSessionDelegate {
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
}
Using the instant messaging function, sendMessage
:
In the WatchKit app
use this code when you want to send information immediately to the iOS
app.
if WCSession.isSupported() {
print("WCSession supported")
let session = WCSession.default()
session.delegate = self
session.activate()
if session.isReachable {
session.sendMessage(["Instant":"Message"], replyHandler: nil, errorHandler: { error in
print("Error sending message",error)
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With