Watch Connectivity

RSS for tag

Implement two-way communication between an iOS app and its paired watchOS app using Watch Connectivity.

Posts under Watch Connectivity tag

195 Posts

Post

Replies

Boosts

Views

Activity

Updating watchOS complication when data in iOS app changes.
Hello, I'm facing problems when attempting to update my watchOS complication when relevant data on the iPhone app changes. From what I gather reading the documentation I have to use the Watch Connectivity Framework to send said new data from the phone to the watch: use transferCurrentComplicationUserInfo() to send a dictionary of data from the phone to the watch implement the didReceiveUserInfo delegate method to handle incoming data on the watch in said handler, save the incoming data to UserDefaults using an App Group so the widget-extension can read that data after saving the data to UserDefaults, call WidgetCenter.shared.reloadAllTimelines() so watchOS can request fresh timelines for my complications change the getTimeline() method of my TimelineProvider so it uses the received data from UserDefaults OR async fetch fresh data if received data from phone is too old If I understand correctly, transferCurrentComplicationUserInfo() is limited to be used a maximum of 50 times a day. I'm running the apps in debug mode, so this should be no problem. Here is my current implementation: 1 : Setup of my WC class: final class Connectivity: NSObject { // singleton approach static let shared = Connectivity() // used to rate limit transmissions from phone → watch private var lastSentBalanceContext: Date? = nil private override init() { super.init() // no need to check availability on watchOS #if !os(watchOS) guard WCSession.isSupported() else { return } #endif WCSession.default.delegate = self WCSession.default.activate() } } 2 : The method enabling transmission from phone to watch: #if os(iOS) extension Connectivity: WCSessionDelegate { func sendBalanceContext(sample: HealthData) { guard WCSession.default.activationState == .activated else { return } guard WCSession.default.isWatchAppInstalled else { return } // rate limitat transmissions guard self.lastSentBalanceContext == nil || abs(Date.now.timeIntervalSince(self.lastSentBalanceContext!)) > 10 else { return } if WCSession.default.remainingComplicationUserInfoTransfers > 0 { WCSession.default.transferCurrentComplicationUserInfo([ "context": "balance", "date": sample.date, "burnedActive": sample.burnedActive, // more data... ]) self.lastSentBalanceContext = .now } } // boilerplate handlers here } #endif 3 : Delegete method that handles incoming data on the watch: #if os(watchOS) extension Connectivity: WCSessionDelegate { func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { guard let context = userInfo["context"] as? String, context == "balance" else { return } guard let date = userInfo["date"] as? Date, let burnedActive = userInfo["burnedActive"] as? Int /* more data... */ else { return } guard let SharedDefaults = UserDefaults(suiteName: "group.4DXABR577J.com.count.kcal.app") else { return } // TimelineProvider uses this to determine wether to use this data or fetch data on its own SharedDefaults.set(Date.now, forKey: "lastReceivedBalanceContext") SharedDefaults.set(date, forKey: "date") SharedDefaults.set(burnedActive, forKey: "burnedActive") // more data... WidgetCenter.shared.reloadAllTimelines() } // boilerplate handlers } #endif 4 : Finally, the TimelineProvider: struct HealthDataEntry: TimelineEntry { let date: Date let data: HealthData } struct HealthDataTimelineProvider: TimelineProvider { // other callbacks here func getTimeline(in context: Context, completion: @escaping (Timeline<HealthDataEntry>) -> Void) { let SharedDefaults: UserDefaults = UserDefaults(suiteName: "group.4DXABR577J.com.count.kcal.app")! let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: .now)! // use data from phone if it is less than 60 seconds old if let lastReceivedBalanceContext = SharedDefaults.object(forKey: "lastReceivedBalanceContext") as? Date { let interval = lastReceivedBalanceContext.timeIntervalSince(.now) if interval > -60 && interval <= 0 { let data = HealthData(date: SharedDefaults.object(forKey: "date") as? Date ?? Date(timeIntervalSinceReferenceDate: 0), burnedActive: SharedDefaults.integer(forKey: "burnedActive"), burnedActive7: SharedDefaults.integer(forKey: "burnedActive7") /* other data ... */) let timeline = Timeline( entries: [HealthDataEntry(date: .now, data: data)], policy: .after(nextUpdateDate) ) completion(timeline) return } } // default: fetch from HealthKit (if received data from phone is > 60s) Task { let timeline = Timeline( entries: [HealthDataEntry(date: .now, data: try! await asyncFetchData())], policy: .after(nextUpdateDate) ) completion(timeline) } } } The issue I am facing is that the watchOS complication only gets refreshed when I acitvely build and run the watchOS app in Xcode and then initiate a transmission of data to the watch. This works even if I do it back to back to back. As soon as I stop the watchOS app from within Xcode, my complications won't update anymore. I noticed this behavior when I used print() statements throughout my code to see whether it is beeing executed as expected. The iPhone sends data, the watch receives it but then the watch fails to update the complications ONLY when not running from Xcode. Can you spot any flaws in my implementation or in my understanding? Maybe transferCurrentComplicationUserInfo() just isn't as reliable as I think it should be? I interpreted it as being practically guaranteed to refresh the complications 50 times a day, pretty much instantly? Any help or guidance would be greatly appreciated!
1
0
2k
Jul ’24
WatchConnectivity Session becomes unreachable but shouldn't
So for context I am building an app where the Apple Watch establishes a WatchConnectivity session with the parent iPhone app and streams audio data from the watch Mic to the iPhone for processing on Deepgram to perform STT. This works very well, unless I tilt my wrist and the display goes to sleep. What I find strange is that due to the mic being in use on my watch, the app is still showing on the always on display and is still trying to send the audio data to my phone in the background (which is perfect!) but the iPhone does not seem to be responding. So my watch code is: private func sendData(_ data: Data) { let dict: [String: Any] = ["audioData": data] session.sendMessage(dict, replyHandler: nil, errorHandler: { error in print("Failed to send data: \(error.localizedDescription)") }) } and my Xcode logs Failed to send data: WatchConnectivity session on paired device is not reachable. So the watch is still running the process but because session.isReachable is false the audio data is not sent which it should be! Is there any way to keep the connection established and data sharing when the display dims?
0
0
852
Jul ’24
Trigger data transfer from watchOS when connectivity is restored
Hello, I have an iOS app and a companion watchOS app. Users record a workout on Apple Watch, the data for which is then transferred using both Watch Connectivity and Core Data + CloudKit (NSPersistentCloudKitContainer) to their iPhone, where it is processed and displayed. As users are recording the workout on their Apple Watch, when they finish and the transfer begins, their iPhone is often not reachable to immediately send the data using Watch Connectivity and they have no network connection (cellular or Wi-Fi). With Watch Connectivity I use transferFile from WCSession, which queues the file for transfer. With Core Data + Cloudkit I save the data and the export is queued. An undetermined amount of time may pass until the user returns to their iPhone or connects to Wi-Fi and most of the time neither of the transfer methods actually transfers the data until the user opens the watchOS app into the foreground, at which point the transfer happens immediately for both methods. I've tried a number of things already, without success, such as: Using sendMessage from WCSession to send an immediate message to the watchOS app when the iOS app returns to the foreground to try and wake the watchOS app up so it can complete the data transfer. On the watchOS app, after attempting to transfer the data, using downloadTask from URLSession to queue a background task to download something, in the hope that it would wake the watchOS app when network connectivity was restored and enable it to complete the data transfer. On the watchOS app, instead of saving the data using NSPersistentCloudKitContainer, using CKRecord and CKDatabase directly to save the data using userInitiated as the quality of service, in the hope that it would be exported once network connectivity was restored. Is there a way to trigger the watchOS app to transfer the data using Watch Connectivity or Core Data + CloudKit in the background when reachabillity or network connectivity is restored, even if the app may have been suspended by watchOS? Many Thanks, Alex
1
0
1.1k
Jul ’24
File transfer to watch fails after OS update
When transferring files from iPhone to Watch, if you run the app and try to transfer after updating the OS, the transfer may not be possible. (or transmission seems to be too slow) Just completely close the app, restart it, and try again. Does anyone know the cause or solution to this problem? It happened after the last 17.5 update and today after the 17.5.1 update. The app doesn't crash or anything, and it doesn't happen often, so I didn't send any feedback or anything, but it's strange.
3
0
1.3k
Jun ’24
A case against TN3135 WatchOS restrictions (NWPathMonitor)
REF: TN3135 Context: Stand alone watch app Target platform is cellular watch Phone likely (90%) out of range in a different location (although this applies equally to watch+phone too) User story: As a water & wind sports enthusiast who is either in-shore, near-shore or off-shore I want to receive near-real-time wind reports on my wrist so that I can determine ...(many varieties of facts) My Case for lifting restrictions on NWPathMonitor, NWPath, NWPath.Status What this is about: Proactive UX (enlightening) vs. Reactive UX (disappointing) Reducing unnecessary code execution proactively Exactly the same purpose the tiny red x at the top center of the Watch screen serves (notifies me that the watch is offline -- probably using NWPath.Status of .unsatisfied) What this is NOT about Preflighting requests UI design (although there is a UI component associated with UX - like the tiny red x ...) Establishing low level connections Watch App End User Scenario Water & Wind sports enthusiasts are frequently in and out of cellular range. As a kiteboarding enthusiast I am relating my personal experience. The phone is in the vehicle in the parking lot > 100 yards away while I'm standing on the beach (before I get into the water). On shore or just off shore my watch likely has a solid connection. When 100 yards off shore watch has a decently but tenuous connection. While at 200 yards off shore watch has no connection at all. Developer's Case Current REACTIVE solution My current watch app is forced to be reactive by attempting a URLSession request and capturing one of the plethora of URLError error conditions. This action, of course, is activated through a user interaction workflow: User interaction --> create URL --> use URLSession --> capture URLError --> determine failure cause --> notify user network data cannot be retrieved Optimal PROACTIVE solution Provide a simple indicator to the end user that the data cannot be retrieved. The reason is not relevant to the end user, but they know their interaction with the app is unnecessary. The app's UX has been improved by proactively precluding an unnecessary interaction and precluding unecessary code execution behind the scenes. NWPath.Status = .unsatisfied --> UI shows "no network" type indicator --> (no user interaction - no backend requests - no code execution) --> NWPath.Status = .satisfied --> UI shows nominal status --> end user interacts normally Rationale Using NWPath.Status allows us as developers to fulfill some basic tenets of the Human Interface Guidelines associated with Providing Feedback Specifically, the overview states we should communicate: The current status of something A warning about an action that can have negative consequences And quoting about my specific use case, the guidelines continue: ... it often works well to display status information in a passive way so that people can view it when they need it. Consider integrating status feedback into your interface. Show people when a command can’t be carried out and help them understand why. And finally, the guideline specifically calls out the WatchOS experience I am attempting to avoid. By proactively providing feedback we can prevent the reactive "touch --> wait & see --> then disappoint with no connection" approach. Final Thoughts I realize I am naive about the behind the scenes with this API. I realize that this is likely not the intended use of this API. But as a developer, I also realize users of our stuff often use it in ways we never intended. Please allow these API features on WatchOS
9
1
1.3k
Jun ’24
7014 - Payload could not be delivered.
Hi All, can anyone help me to the below issue? i used the WatchConnectivity to send data from iphone to watch app. Now i tried to run them on emulator (watch series 5, and iphone 15 pro) I have an error when i tried to send message data from iphone app (react-native) to watch app: { "code": "EWCERRORDOMAIN7014", "domain": "WCErrorDomain", "message": "Payload could not be delivered.", "nativeStackIOS": [ "0 releasev2 0x0000000101979c90 RCTJSErrorFromCodeMessageAndNSError + 112", "1 releasev2 0x0000000101979bd0 RCTJSErrorFromNSError + 256", "2 releasev2 0x000000010190c2b4 __41-[RCTModuleMethod processMethodSignature]_block_invoke_4.110 + 148", "3 releasev2 0x000000010185003d __35-[RNWatch sendMessage:reply:error:]_block_invoke.116 + 77", "4 WatchConnectivity 0x000000011428b176 __70-[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:]_block_invoke + 206", "5 Foundation 0x0000000119095004 NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK + 7", "6 Foundation 0x0000000119094f02 -[NSBlockOperation main] + 94", "7 Foundation 0x0000000119097ef2 NSOPERATION_IS_INVOKING_MAIN + 17", "8 Foundation 0x00000001190940aa -[NSOperation start] + 730", "9 Foundation 0x0000000119098744 NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION + 17", "10 Foundation 0x0000000119098385 __NSOQSchedule_f + 182", "11 libdispatch.dylib 0x000000011434ca90 _dispatch_call_block_and_release + 12", "12 libdispatch.dylib 0x000000011434dd3a _dispatch_client_callout + 8", "13 libdispatch.dylib 0x000000011435126a _dispatch_continuation_pop + 874", "14 libdispatch.dylib 0x00000001143502b0 _dispatch_async_redirect_invoke + 994", "15 libdispatch.dylib 0x000000011436041e _dispatch_root_queue_drain + 372", "16 libdispatch.dylib 0x0000000114360e88 _dispatch_worker_thread2 + 244", "17 libsystem_pthread.dylib 0x0000000116800c0f _pthread_wqthread + 257", "18 libsystem_pthread.dylib 0x00000001167ffbbf start_wqthread + 15" ], "userInfo": { "NSLocalizedDescription": "Payload could not be delivered." } } Here is the class connector shared data on watch target: class SharedDataConnecter: NSObject, ObservableObject { var session: WCSession init(session: WCSession = .default){ self.session = session super.init() if WCSession.isSupported(){ session.delegate = self session.activate() } } } extension SharedDataConnecter: WCSessionDelegate{ func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { print("Active State: ", activationState.rawValue) print("Error: ", error) } func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) { print("AAAA message from app: ", message) } }
0
0
903
Jun ’24
Xcode can not consistently connect with WatchOS
When testing my single target watchOS app, I can at best get about one minute of connect time after installing my app on my Apple Watch Series 4. After about a minute or less, Xcode loses it connection to the watch and it becomes disconnected. When attempting to reconnect it gets transport errors. I can restart my Mac and reconnect again, but again only for about a minute before the transport error occurs again. I'm using Xcode 15.4 on a MacOS 14.5 and a Apple Watch Series 4 running 10.5
2
0
907
Jun ’24
Is it possible to change bundle id of Watch kit app?
Long story short, I had my App and Watch app already uploaded to the app store. However, I needed to add a WatchConnectivity to have App to Watch communication. At the beginning My app bundle id was: com.x My watch bundle id was: com.x.watchkitapp However, while developing Watch Connectivity, I noticed that my Apps are not connected unless I changed it to com.x.watchkitapp -> com.x.watch However after changing it I cannot submit my bundle anymore. I'm getting Asset validation failed error Invalid Bundle Identifier. Attempting to change bundle identifier from com.x.watchkitapp to com.x.watch is disallowed for bundle x.app/Watch/WatchX Watch App.app. (ID: 75a4621a-7e28-411d-a2a7-84674e460656) Any ideas how this could be solved?
1
0
919
May ’24
Get rid of the task time displayed by the system
When I run my app in debug mode, whenever a time consuming task is run (like a core data fetch request) there is a time indication appearing on top of the view. I am unable to find the meaning of this and how it is named. It looks like: "appName NNNN ms" (it is not caught by the screen shot) Can someone tell if it is possible to get rid of it and how ? Thanks for your help !
1
0
1.1k
May ’24
Wish apple unleash the power of watch
As TN3135 clearly explains the limitations apple puts on the low level networking, it doesn’t really give a reason. Presumably the power consumption problem. But as the battery technology continues evolving, it could be exciting that apple might loose the restrictions someday. The watch itself is powerful enough to do a lot of sophisticated works, sure it works best with companion apps on iPhone, but even as a standalone device, we can still provide many advanced user experience with low level networking supports. wish apple guys can read it and give a consideration.
0
0
663
May ’24
Error event - Minimed Mobile App . Triggered by insufficient update interval of the Watch iOS system of only 50 updates/day problem with update interval of the Apple Watch iOS
Criticism of the use of the Minimed Mobile App, problem with the update interval of the Apple Watch iOS Subject: Error event - Minimed Mobile App . Triggered by insufficient update interval of the Watch iOS system of only 50 updates/day Dear Sir or Madam, I use the Minimed Mobile App in conjunction with my Medtronic 780 G insulin pump and am generally very satisfied with the app. However, I have noticed a point here that is caused by the Apple Watch iOS operating system. I would like to pass this on and ask that you please fix this problem with an update. Updating the sensor values on watch faces on the Apple Watch I use the Modular widget on my Apple Watch in conjunction with the Minimed mobile app to quickly and easily keep an eye on the current sensor value. Unfortunately, the sensor value in this widget is not updated automatically according to someone in the know, Apple only updates widget information 50 times a day. After a short time, a rotating arrow appears instead of the current value, which I have to press to get a sensor value displayed again in order to see the current value. This is very inconvenient as I have to perform several actions each time to see the current value: 1.Open widget 2.Press the rotary arrow 3.Read the value Close widget by pressing the spin button at the top right to return to the main screen I would very much welcome it if the sensor value in Apple Watch watch faces were updated automatically, the problem is obvious - the watch faces only update 50 times a day. This would prevent me from seeing the current sensor value quickly and easily at any time without having to perform additional and cumbersome actions. The Minimed Mobile App has been tested and approved by Medtronic through Apple. However, it must have been overlooked that this sensor value app updates the sensor value every 5 minutes, but it does not do this on the dial because, according to my friend, it only updates 50 times a day. Now it is the case that the sensor value can change every 5 minutes, but this is not shown on the watch face, which means that the Apple Watch iOS would need 288 updates for this Minimed Mobile App per day instead of only 50 updates per day. Can you please advocate for a new update that allows an update interval of up to 288 updates per day? So that thousands of diabetics worldwide can see current values on their Apple Watch dials at any time? Without having to take several steps each time? I switched to I Phone and Apple Watch because I thought this would be possible for me. I would be very happy to hear back if this is technically possible for all diabetics worldwide who will benefit from it. With kind regards
2
0
1k
May ’24
watchOS live audio stream from watch
Hi, I would like to stream live audio (hls) from watch itself, our app can start stream on paired device, but when phone is not near by i want to start streaming on watchos (just like spotify or Apple music app) I watched the 2019 wwdc video about streaming and also looked to the documentation Documentation : https://developer.apple.com/documentation/watchkit/storyboard_support/playing_background_audio I can present the route controller to select output, but for example after selecting Air Podcas, stream did not start.. Here is the code: (I have enabled background mode audio) do { try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, policy: .longFormAudio, options: []) } catch { print("no audiosession!") } AVAudioSession.sharedInstance().activate(options: []) { success, error in dump(success) dump(error) DispatchQueue.main.async { if let streamURL = moduleItem.media?[0] as? String { dump(streamURL) let asset = AVURLAsset(url: URL(string: streamURL)!, options: nil) let item = AVPlayerItem(asset: asset) let player = AVQueuePlayer(playerItem: item) player.play() } } }
0
1
905
May ’24
Xcode 15 - Apple Watch Companion Embed Not Working
The iPhone Target Build Phase for my Apple Watch companion app specifies the source of the Product to embed as: build/Debug-watchos, however, the Watch build is located at build/Debug-watchsimulator. I am receiving an error from Xcode when trying to install the Apple Watch app in the watch simulator, as follows: An application bundle was not found at the provided path. Provide a valid path to the desired application bundle. Failed to install the requested application Domain: NSPOSIXErrorDomain Code: 2 Failure Reason: An application bundle was not found at the provided path. Recovery Suggestion: Provide a valid path to the desired application bundle. I have tried deleting the Watch target and re-adding the target but the misconfiguration remains. I have also tried adding my own Copy Build Phase but when selecting the Product for the Apple Watch companion app the same incorrect folder is used. Any ideas?
14
3
5.5k
Apr ’24
Watch Session Reachability Issue with BLE Data Stream
I am trying to develop a watch app and got all the plumbing in place. I successfully send messages from the phone to the watch and see updates reflected in the UI. However when I connect to a device that streams data over bluetooth, using the same messaging interface the data fails to transfer. It appears WCSession.default.isReachable becomes false as soon as this BLE stream starts. I suspect the app is prioritizing the BLE radio for this instead of connectivity to the watch? Is my assumption valid? Are there any workaround to this?
0
0
506
Apr ’24
WatchConnectivity
Good morning, I come to you for a question: When I install my application on my iPhone for the first time, and I install the watch application from the native "Watch" application, the Watch Connectivity function does not work, I have to do the installation from Xcode to the watch for this function to work. Is this normal? if yes, the problem will not arise during a publication? I have the same problem when using watch and iPhone simulators, WatchConnectivity does not work. I am this error code in Xcode: -[WCSession handleIncomingUserInfoWithPairingID:]_block_invoke delegate (null) does not implement session:didReceiveUserInfo:, discarding incoming content Here is the code for the iPhone and the watch: In my iPhone app: import WatchConnectivity let userDefaultsDataVenantWatch = UserDefaults.standard class PhoneDataModel : NSObject, WCSessionDelegate, ObservableObject { static let shared = PhoneDataModel() let session = WCSession.default @Published var TableauSynchroIphoneVersWatch : [String:String] = ["0":"0"] @Published var dataWatchVersIphone: [String:String] = ["":""] override init() { super.init() if WCSession.isSupported() { session.delegate = self session.activate() } else { print("ERROR: Watch session not supported") } } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { if let error = error { print("session activation failed with error: \(error.localizedDescription)") return } } func sessionDidBecomeInactive(_ session: WCSession) { session.activate() } func sessionDidDeactivate(_ session: WCSession) { session.activate() } func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { guard let newCount = userInfo["TableauSynchroIphoneWatch"] as? [String:String] else { print("ERROR: unknown data received from Watch TableauSynchroIphoneWatch") return } DispatchQueue.main.async { print(newCount) } } } In my Watch app: import WatchConnectivity let userDefaultsDataVenantIphone = UserDefaults.standard var TableauVenantIphone:[String:String] = ["":""] class WatchDataModel : NSObject, WCSessionDelegate, ObservableObject { static let shared = WatchDataModel() let session = WCSession.default @Published var TableauSynchroIphoneWatch : [String:String] = ["0":"0"] override init() { super.init() if WCSession.isSupported() { session.delegate = self session.activate() } else { print("ERROR: Watch session not supported") } } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { if let error = error { print("session activation failed with error: \(error.localizedDescription)") return } } func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { guard let newCount = userInfo["TableauSynchroIphoneVersWatch"] as? [String:String] else { print("ERROR: unknown data received from Watch TableauSynchroIphoneWatch") return } DispatchQueue.main.async { print(newCount) } } } Thank for your answers !
2
0
1.1k
Apr ’24
iOS 18 not connecting to watch
Bought an Apple Watch Ultra 2 and the phone will not recognize it in pairing mode. both devices have been reset multiple times
Replies
0
Boosts
0
Views
615
Activity
Aug ’24
Updating watchOS complication when data in iOS app changes.
Hello, I'm facing problems when attempting to update my watchOS complication when relevant data on the iPhone app changes. From what I gather reading the documentation I have to use the Watch Connectivity Framework to send said new data from the phone to the watch: use transferCurrentComplicationUserInfo() to send a dictionary of data from the phone to the watch implement the didReceiveUserInfo delegate method to handle incoming data on the watch in said handler, save the incoming data to UserDefaults using an App Group so the widget-extension can read that data after saving the data to UserDefaults, call WidgetCenter.shared.reloadAllTimelines() so watchOS can request fresh timelines for my complications change the getTimeline() method of my TimelineProvider so it uses the received data from UserDefaults OR async fetch fresh data if received data from phone is too old If I understand correctly, transferCurrentComplicationUserInfo() is limited to be used a maximum of 50 times a day. I'm running the apps in debug mode, so this should be no problem. Here is my current implementation: 1 : Setup of my WC class: final class Connectivity: NSObject { // singleton approach static let shared = Connectivity() // used to rate limit transmissions from phone → watch private var lastSentBalanceContext: Date? = nil private override init() { super.init() // no need to check availability on watchOS #if !os(watchOS) guard WCSession.isSupported() else { return } #endif WCSession.default.delegate = self WCSession.default.activate() } } 2 : The method enabling transmission from phone to watch: #if os(iOS) extension Connectivity: WCSessionDelegate { func sendBalanceContext(sample: HealthData) { guard WCSession.default.activationState == .activated else { return } guard WCSession.default.isWatchAppInstalled else { return } // rate limitat transmissions guard self.lastSentBalanceContext == nil || abs(Date.now.timeIntervalSince(self.lastSentBalanceContext!)) > 10 else { return } if WCSession.default.remainingComplicationUserInfoTransfers > 0 { WCSession.default.transferCurrentComplicationUserInfo([ "context": "balance", "date": sample.date, "burnedActive": sample.burnedActive, // more data... ]) self.lastSentBalanceContext = .now } } // boilerplate handlers here } #endif 3 : Delegete method that handles incoming data on the watch: #if os(watchOS) extension Connectivity: WCSessionDelegate { func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { guard let context = userInfo["context"] as? String, context == "balance" else { return } guard let date = userInfo["date"] as? Date, let burnedActive = userInfo["burnedActive"] as? Int /* more data... */ else { return } guard let SharedDefaults = UserDefaults(suiteName: "group.4DXABR577J.com.count.kcal.app") else { return } // TimelineProvider uses this to determine wether to use this data or fetch data on its own SharedDefaults.set(Date.now, forKey: "lastReceivedBalanceContext") SharedDefaults.set(date, forKey: "date") SharedDefaults.set(burnedActive, forKey: "burnedActive") // more data... WidgetCenter.shared.reloadAllTimelines() } // boilerplate handlers } #endif 4 : Finally, the TimelineProvider: struct HealthDataEntry: TimelineEntry { let date: Date let data: HealthData } struct HealthDataTimelineProvider: TimelineProvider { // other callbacks here func getTimeline(in context: Context, completion: @escaping (Timeline<HealthDataEntry>) -> Void) { let SharedDefaults: UserDefaults = UserDefaults(suiteName: "group.4DXABR577J.com.count.kcal.app")! let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: .now)! // use data from phone if it is less than 60 seconds old if let lastReceivedBalanceContext = SharedDefaults.object(forKey: "lastReceivedBalanceContext") as? Date { let interval = lastReceivedBalanceContext.timeIntervalSince(.now) if interval > -60 && interval <= 0 { let data = HealthData(date: SharedDefaults.object(forKey: "date") as? Date ?? Date(timeIntervalSinceReferenceDate: 0), burnedActive: SharedDefaults.integer(forKey: "burnedActive"), burnedActive7: SharedDefaults.integer(forKey: "burnedActive7") /* other data ... */) let timeline = Timeline( entries: [HealthDataEntry(date: .now, data: data)], policy: .after(nextUpdateDate) ) completion(timeline) return } } // default: fetch from HealthKit (if received data from phone is > 60s) Task { let timeline = Timeline( entries: [HealthDataEntry(date: .now, data: try! await asyncFetchData())], policy: .after(nextUpdateDate) ) completion(timeline) } } } The issue I am facing is that the watchOS complication only gets refreshed when I acitvely build and run the watchOS app in Xcode and then initiate a transmission of data to the watch. This works even if I do it back to back to back. As soon as I stop the watchOS app from within Xcode, my complications won't update anymore. I noticed this behavior when I used print() statements throughout my code to see whether it is beeing executed as expected. The iPhone sends data, the watch receives it but then the watch fails to update the complications ONLY when not running from Xcode. Can you spot any flaws in my implementation or in my understanding? Maybe transferCurrentComplicationUserInfo() just isn't as reliable as I think it should be? I interpreted it as being practically guaranteed to refresh the complications 50 times a day, pretty much instantly? Any help or guidance would be greatly appreciated!
Replies
1
Boosts
0
Views
2k
Activity
Jul ’24
Ultra Complications disabled
I have installed IOS18 and WatchOS11 Beta 3 already... all complications sent to clock (usign FACER app) is not showing up... they all appears DISABLED. doesnt matter what face watch i use.., its all disabled. Any help on it please????
Replies
3
Boosts
1
Views
1.1k
Activity
Jul ’24
WatchConnectivity Session becomes unreachable but shouldn't
So for context I am building an app where the Apple Watch establishes a WatchConnectivity session with the parent iPhone app and streams audio data from the watch Mic to the iPhone for processing on Deepgram to perform STT. This works very well, unless I tilt my wrist and the display goes to sleep. What I find strange is that due to the mic being in use on my watch, the app is still showing on the always on display and is still trying to send the audio data to my phone in the background (which is perfect!) but the iPhone does not seem to be responding. So my watch code is: private func sendData(_ data: Data) { let dict: [String: Any] = ["audioData": data] session.sendMessage(dict, replyHandler: nil, errorHandler: { error in print("Failed to send data: \(error.localizedDescription)") }) } and my Xcode logs Failed to send data: WatchConnectivity session on paired device is not reachable. So the watch is still running the process but because session.isReachable is false the audio data is not sent which it should be! Is there any way to keep the connection established and data sharing when the display dims?
Replies
0
Boosts
0
Views
852
Activity
Jul ’24
Trigger data transfer from watchOS when connectivity is restored
Hello, I have an iOS app and a companion watchOS app. Users record a workout on Apple Watch, the data for which is then transferred using both Watch Connectivity and Core Data + CloudKit (NSPersistentCloudKitContainer) to their iPhone, where it is processed and displayed. As users are recording the workout on their Apple Watch, when they finish and the transfer begins, their iPhone is often not reachable to immediately send the data using Watch Connectivity and they have no network connection (cellular or Wi-Fi). With Watch Connectivity I use transferFile from WCSession, which queues the file for transfer. With Core Data + Cloudkit I save the data and the export is queued. An undetermined amount of time may pass until the user returns to their iPhone or connects to Wi-Fi and most of the time neither of the transfer methods actually transfers the data until the user opens the watchOS app into the foreground, at which point the transfer happens immediately for both methods. I've tried a number of things already, without success, such as: Using sendMessage from WCSession to send an immediate message to the watchOS app when the iOS app returns to the foreground to try and wake the watchOS app up so it can complete the data transfer. On the watchOS app, after attempting to transfer the data, using downloadTask from URLSession to queue a background task to download something, in the hope that it would wake the watchOS app when network connectivity was restored and enable it to complete the data transfer. On the watchOS app, instead of saving the data using NSPersistentCloudKitContainer, using CKRecord and CKDatabase directly to save the data using userInitiated as the quality of service, in the hope that it would be exported once network connectivity was restored. Is there a way to trigger the watchOS app to transfer the data using Watch Connectivity or Core Data + CloudKit in the background when reachabillity or network connectivity is restored, even if the app may have been suspended by watchOS? Many Thanks, Alex
Replies
1
Boosts
0
Views
1.1k
Activity
Jul ’24
File transfer to watch fails after OS update
When transferring files from iPhone to Watch, if you run the app and try to transfer after updating the OS, the transfer may not be possible. (or transmission seems to be too slow) Just completely close the app, restart it, and try again. Does anyone know the cause or solution to this problem? It happened after the last 17.5 update and today after the 17.5.1 update. The app doesn't crash or anything, and it doesn't happen often, so I didn't send any feedback or anything, but it's strange.
Replies
3
Boosts
0
Views
1.3k
Activity
Jun ’24
A case against TN3135 WatchOS restrictions (NWPathMonitor)
REF: TN3135 Context: Stand alone watch app Target platform is cellular watch Phone likely (90%) out of range in a different location (although this applies equally to watch+phone too) User story: As a water & wind sports enthusiast who is either in-shore, near-shore or off-shore I want to receive near-real-time wind reports on my wrist so that I can determine ...(many varieties of facts) My Case for lifting restrictions on NWPathMonitor, NWPath, NWPath.Status What this is about: Proactive UX (enlightening) vs. Reactive UX (disappointing) Reducing unnecessary code execution proactively Exactly the same purpose the tiny red x at the top center of the Watch screen serves (notifies me that the watch is offline -- probably using NWPath.Status of .unsatisfied) What this is NOT about Preflighting requests UI design (although there is a UI component associated with UX - like the tiny red x ...) Establishing low level connections Watch App End User Scenario Water & Wind sports enthusiasts are frequently in and out of cellular range. As a kiteboarding enthusiast I am relating my personal experience. The phone is in the vehicle in the parking lot > 100 yards away while I'm standing on the beach (before I get into the water). On shore or just off shore my watch likely has a solid connection. When 100 yards off shore watch has a decently but tenuous connection. While at 200 yards off shore watch has no connection at all. Developer's Case Current REACTIVE solution My current watch app is forced to be reactive by attempting a URLSession request and capturing one of the plethora of URLError error conditions. This action, of course, is activated through a user interaction workflow: User interaction --> create URL --> use URLSession --> capture URLError --> determine failure cause --> notify user network data cannot be retrieved Optimal PROACTIVE solution Provide a simple indicator to the end user that the data cannot be retrieved. The reason is not relevant to the end user, but they know their interaction with the app is unnecessary. The app's UX has been improved by proactively precluding an unnecessary interaction and precluding unecessary code execution behind the scenes. NWPath.Status = .unsatisfied --> UI shows "no network" type indicator --> (no user interaction - no backend requests - no code execution) --> NWPath.Status = .satisfied --> UI shows nominal status --> end user interacts normally Rationale Using NWPath.Status allows us as developers to fulfill some basic tenets of the Human Interface Guidelines associated with Providing Feedback Specifically, the overview states we should communicate: The current status of something A warning about an action that can have negative consequences And quoting about my specific use case, the guidelines continue: ... it often works well to display status information in a passive way so that people can view it when they need it. Consider integrating status feedback into your interface. Show people when a command can’t be carried out and help them understand why. And finally, the guideline specifically calls out the WatchOS experience I am attempting to avoid. By proactively providing feedback we can prevent the reactive "touch --> wait & see --> then disappoint with no connection" approach. Final Thoughts I realize I am naive about the behind the scenes with this API. I realize that this is likely not the intended use of this API. But as a developer, I also realize users of our stuff often use it in ways we never intended. Please allow these API features on WatchOS
Replies
9
Boosts
1
Views
1.3k
Activity
Jun ’24
7014 - Payload could not be delivered.
Hi All, can anyone help me to the below issue? i used the WatchConnectivity to send data from iphone to watch app. Now i tried to run them on emulator (watch series 5, and iphone 15 pro) I have an error when i tried to send message data from iphone app (react-native) to watch app: { "code": "EWCERRORDOMAIN7014", "domain": "WCErrorDomain", "message": "Payload could not be delivered.", "nativeStackIOS": [ "0 releasev2 0x0000000101979c90 RCTJSErrorFromCodeMessageAndNSError + 112", "1 releasev2 0x0000000101979bd0 RCTJSErrorFromNSError + 256", "2 releasev2 0x000000010190c2b4 __41-[RCTModuleMethod processMethodSignature]_block_invoke_4.110 + 148", "3 releasev2 0x000000010185003d __35-[RNWatch sendMessage:reply:error:]_block_invoke.116 + 77", "4 WatchConnectivity 0x000000011428b176 __70-[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:]_block_invoke + 206", "5 Foundation 0x0000000119095004 NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK + 7", "6 Foundation 0x0000000119094f02 -[NSBlockOperation main] + 94", "7 Foundation 0x0000000119097ef2 NSOPERATION_IS_INVOKING_MAIN + 17", "8 Foundation 0x00000001190940aa -[NSOperation start] + 730", "9 Foundation 0x0000000119098744 NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION + 17", "10 Foundation 0x0000000119098385 __NSOQSchedule_f + 182", "11 libdispatch.dylib 0x000000011434ca90 _dispatch_call_block_and_release + 12", "12 libdispatch.dylib 0x000000011434dd3a _dispatch_client_callout + 8", "13 libdispatch.dylib 0x000000011435126a _dispatch_continuation_pop + 874", "14 libdispatch.dylib 0x00000001143502b0 _dispatch_async_redirect_invoke + 994", "15 libdispatch.dylib 0x000000011436041e _dispatch_root_queue_drain + 372", "16 libdispatch.dylib 0x0000000114360e88 _dispatch_worker_thread2 + 244", "17 libsystem_pthread.dylib 0x0000000116800c0f _pthread_wqthread + 257", "18 libsystem_pthread.dylib 0x00000001167ffbbf start_wqthread + 15" ], "userInfo": { "NSLocalizedDescription": "Payload could not be delivered." } } Here is the class connector shared data on watch target: class SharedDataConnecter: NSObject, ObservableObject { var session: WCSession init(session: WCSession = .default){ self.session = session super.init() if WCSession.isSupported(){ session.delegate = self session.activate() } } } extension SharedDataConnecter: WCSessionDelegate{ func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { print("Active State: ", activationState.rawValue) print("Error: ", error) } func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) { print("AAAA message from app: ", message) } }
Replies
0
Boosts
0
Views
903
Activity
Jun ’24
Xcode can not consistently connect with WatchOS
When testing my single target watchOS app, I can at best get about one minute of connect time after installing my app on my Apple Watch Series 4. After about a minute or less, Xcode loses it connection to the watch and it becomes disconnected. When attempting to reconnect it gets transport errors. I can restart my Mac and reconnect again, but again only for about a minute before the transport error occurs again. I'm using Xcode 15.4 on a MacOS 14.5 and a Apple Watch Series 4 running 10.5
Replies
2
Boosts
0
Views
907
Activity
Jun ’24
Is it possible to change bundle id of Watch kit app?
Long story short, I had my App and Watch app already uploaded to the app store. However, I needed to add a WatchConnectivity to have App to Watch communication. At the beginning My app bundle id was: com.x My watch bundle id was: com.x.watchkitapp However, while developing Watch Connectivity, I noticed that my Apps are not connected unless I changed it to com.x.watchkitapp -> com.x.watch However after changing it I cannot submit my bundle anymore. I'm getting Asset validation failed error Invalid Bundle Identifier. Attempting to change bundle identifier from com.x.watchkitapp to com.x.watch is disallowed for bundle x.app/Watch/WatchX Watch App.app. (ID: 75a4621a-7e28-411d-a2a7-84674e460656) Any ideas how this could be solved?
Replies
1
Boosts
0
Views
919
Activity
May ’24
Get rid of the task time displayed by the system
When I run my app in debug mode, whenever a time consuming task is run (like a core data fetch request) there is a time indication appearing on top of the view. I am unable to find the meaning of this and how it is named. It looks like: "appName NNNN ms" (it is not caught by the screen shot) Can someone tell if it is possible to get rid of it and how ? Thanks for your help !
Replies
1
Boosts
0
Views
1.1k
Activity
May ’24
Wish apple unleash the power of watch
As TN3135 clearly explains the limitations apple puts on the low level networking, it doesn’t really give a reason. Presumably the power consumption problem. But as the battery technology continues evolving, it could be exciting that apple might loose the restrictions someday. The watch itself is powerful enough to do a lot of sophisticated works, sure it works best with companion apps on iPhone, but even as a standalone device, we can still provide many advanced user experience with low level networking supports. wish apple guys can read it and give a consideration.
Replies
0
Boosts
0
Views
663
Activity
May ’24
Error event - Minimed Mobile App . Triggered by insufficient update interval of the Watch iOS system of only 50 updates/day problem with update interval of the Apple Watch iOS
Criticism of the use of the Minimed Mobile App, problem with the update interval of the Apple Watch iOS Subject: Error event - Minimed Mobile App . Triggered by insufficient update interval of the Watch iOS system of only 50 updates/day Dear Sir or Madam, I use the Minimed Mobile App in conjunction with my Medtronic 780 G insulin pump and am generally very satisfied with the app. However, I have noticed a point here that is caused by the Apple Watch iOS operating system. I would like to pass this on and ask that you please fix this problem with an update. Updating the sensor values on watch faces on the Apple Watch I use the Modular widget on my Apple Watch in conjunction with the Minimed mobile app to quickly and easily keep an eye on the current sensor value. Unfortunately, the sensor value in this widget is not updated automatically according to someone in the know, Apple only updates widget information 50 times a day. After a short time, a rotating arrow appears instead of the current value, which I have to press to get a sensor value displayed again in order to see the current value. This is very inconvenient as I have to perform several actions each time to see the current value: 1.Open widget 2.Press the rotary arrow 3.Read the value Close widget by pressing the spin button at the top right to return to the main screen I would very much welcome it if the sensor value in Apple Watch watch faces were updated automatically, the problem is obvious - the watch faces only update 50 times a day. This would prevent me from seeing the current sensor value quickly and easily at any time without having to perform additional and cumbersome actions. The Minimed Mobile App has been tested and approved by Medtronic through Apple. However, it must have been overlooked that this sensor value app updates the sensor value every 5 minutes, but it does not do this on the dial because, according to my friend, it only updates 50 times a day. Now it is the case that the sensor value can change every 5 minutes, but this is not shown on the watch face, which means that the Apple Watch iOS would need 288 updates for this Minimed Mobile App per day instead of only 50 updates per day. Can you please advocate for a new update that allows an update interval of up to 288 updates per day? So that thousands of diabetics worldwide can see current values on their Apple Watch dials at any time? Without having to take several steps each time? I switched to I Phone and Apple Watch because I thought this would be possible for me. I would be very happy to hear back if this is technically possible for all diabetics worldwide who will benefit from it. With kind regards
Replies
2
Boosts
0
Views
1k
Activity
May ’24
watchOS live audio stream from watch
Hi, I would like to stream live audio (hls) from watch itself, our app can start stream on paired device, but when phone is not near by i want to start streaming on watchos (just like spotify or Apple music app) I watched the 2019 wwdc video about streaming and also looked to the documentation Documentation : https://developer.apple.com/documentation/watchkit/storyboard_support/playing_background_audio I can present the route controller to select output, but for example after selecting Air Podcas, stream did not start.. Here is the code: (I have enabled background mode audio) do { try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, policy: .longFormAudio, options: []) } catch { print("no audiosession!") } AVAudioSession.sharedInstance().activate(options: []) { success, error in dump(success) dump(error) DispatchQueue.main.async { if let streamURL = moduleItem.media?[0] as? String { dump(streamURL) let asset = AVURLAsset(url: URL(string: streamURL)!, options: nil) let item = AVPlayerItem(asset: asset) let player = AVQueuePlayer(playerItem: item) player.play() } } }
Replies
0
Boosts
1
Views
905
Activity
May ’24
Xcode 15 - Apple Watch Companion Embed Not Working
The iPhone Target Build Phase for my Apple Watch companion app specifies the source of the Product to embed as: build/Debug-watchos, however, the Watch build is located at build/Debug-watchsimulator. I am receiving an error from Xcode when trying to install the Apple Watch app in the watch simulator, as follows: An application bundle was not found at the provided path. Provide a valid path to the desired application bundle. Failed to install the requested application Domain: NSPOSIXErrorDomain Code: 2 Failure Reason: An application bundle was not found at the provided path. Recovery Suggestion: Provide a valid path to the desired application bundle. I have tried deleting the Watch target and re-adding the target but the misconfiguration remains. I have also tried adding my own Copy Build Phase but when selecting the Product for the Apple Watch companion app the same incorrect folder is used. Any ideas?
Replies
14
Boosts
3
Views
5.5k
Activity
Apr ’24
Watch Session Reachability Issue with BLE Data Stream
I am trying to develop a watch app and got all the plumbing in place. I successfully send messages from the phone to the watch and see updates reflected in the UI. However when I connect to a device that streams data over bluetooth, using the same messaging interface the data fails to transfer. It appears WCSession.default.isReachable becomes false as soon as this BLE stream starts. I suspect the app is prioritizing the BLE radio for this instead of connectivity to the watch? Is my assumption valid? Are there any workaround to this?
Replies
0
Boosts
0
Views
506
Activity
Apr ’24
Apple watch not visible
Despite the iPhone 11 running iOS version 17.4.1 and the Apple Watch Series 3 operating on version 8.8.1, and both devices being connected, the Apple Watch remains undetected within Xcode version 15.3.
Replies
0
Boosts
0
Views
568
Activity
Apr ’24
how do I turn on the developer mode for watchOS 10.0
I am a watchOS 10.0 device, try to open the developer mode, according to the official website to enter the Settings > Privacy > Developer Mode. But I don't see the developer mode option, how do I turn on the developer mode for watchOS 10.0 device?
Replies
2
Boosts
0
Views
1.1k
Activity
Apr ’24
WatchConnectivity
Good morning, I come to you for a question: When I install my application on my iPhone for the first time, and I install the watch application from the native "Watch" application, the Watch Connectivity function does not work, I have to do the installation from Xcode to the watch for this function to work. Is this normal? if yes, the problem will not arise during a publication? I have the same problem when using watch and iPhone simulators, WatchConnectivity does not work. I am this error code in Xcode: -[WCSession handleIncomingUserInfoWithPairingID:]_block_invoke delegate (null) does not implement session:didReceiveUserInfo:, discarding incoming content Here is the code for the iPhone and the watch: In my iPhone app: import WatchConnectivity let userDefaultsDataVenantWatch = UserDefaults.standard class PhoneDataModel : NSObject, WCSessionDelegate, ObservableObject { static let shared = PhoneDataModel() let session = WCSession.default @Published var TableauSynchroIphoneVersWatch : [String:String] = ["0":"0"] @Published var dataWatchVersIphone: [String:String] = ["":""] override init() { super.init() if WCSession.isSupported() { session.delegate = self session.activate() } else { print("ERROR: Watch session not supported") } } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { if let error = error { print("session activation failed with error: \(error.localizedDescription)") return } } func sessionDidBecomeInactive(_ session: WCSession) { session.activate() } func sessionDidDeactivate(_ session: WCSession) { session.activate() } func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { guard let newCount = userInfo["TableauSynchroIphoneWatch"] as? [String:String] else { print("ERROR: unknown data received from Watch TableauSynchroIphoneWatch") return } DispatchQueue.main.async { print(newCount) } } } In my Watch app: import WatchConnectivity let userDefaultsDataVenantIphone = UserDefaults.standard var TableauVenantIphone:[String:String] = ["":""] class WatchDataModel : NSObject, WCSessionDelegate, ObservableObject { static let shared = WatchDataModel() let session = WCSession.default @Published var TableauSynchroIphoneWatch : [String:String] = ["0":"0"] override init() { super.init() if WCSession.isSupported() { session.delegate = self session.activate() } else { print("ERROR: Watch session not supported") } } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { if let error = error { print("session activation failed with error: \(error.localizedDescription)") return } } func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { guard let newCount = userInfo["TableauSynchroIphoneVersWatch"] as? [String:String] else { print("ERROR: unknown data received from Watch TableauSynchroIphoneWatch") return } DispatchQueue.main.async { print(newCount) } } } Thank for your answers !
Replies
2
Boosts
0
Views
1.1k
Activity
Apr ’24
Custom Complication Previews when applying .watchface don't appear
I'm building complications using WidgetKit and SwiftUI. The complications work, but the previews don't appear right away when applying the .watchface. How can I ensure the complication previews show up immediately when a user adds the watch face? Any guidance is appreciated.
Replies
3
Boosts
0
Views
964
Activity
Apr ’24