I am building a watchOS app with iOS companion app.
The watch app needs to track the heart rate during the night or while user is sleeping. And the desired frequency of measurement is 0.2Hz (every 5 seconds)
For this I am using the HKWorkout mode with mindAndBody session.
While it works fine, One of the main issue is: after about 6-7 hours of usage, the battery on the watch drains between 40% (Series 9) and 100% (series 7, I think)
My questions:
Are there any other option to track user's heart rate without workout, while the app could be in background?
Another side effect of this workout mode is, Even if we choose not to save the workout in HealthKit, the Activity rings gets populated by this mindAndBody session, which makes it when the user is waking up, the bar is already full, This is not desired.
Is there any option to specify for ActivityRing skips this?
Highly appreciate any help in advance.
Cheers - Prakash
Watch Connectivity
RSS for tagImplement two-way communication between an iOS app and its paired watchOS app using Watch Connectivity.
Posts under Watch Connectivity tag
58 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
When launching the SimpleWatchConnectivity sample in a simulator (iOS 18.2 + watchOS 11.2), almost all the functionality works, but TransferFile does not work. The error is the following:
-[WCFileStorage persistOutgoingFileTransfer:] error serializing file transfer <WCSessionFileTransfer: ...> due to Error Domain=NSCocoaErrorDomain Code=4866 "Caught exception during archival: This object may only be encoded by an NSXPCCoder."
How can it be fixed or worked around?
Dear Experts,
I have been looking at thr SimpleWatchConnectivity sample code:
https://developer.apple.com/documentation/watchconnectivity/transferring-data-with-watch-connectivity
There are a couple of things in there that look out of date. Firstly, it uses a WKApplicationDelegate to receive the background tasks. I believe this can probably be entirely removed, and replaced with .backgroundTask(.watchConnectivity) { ... } on the App. Is that true? What do I need something inside the { ... } there?
Secondly, it is using NSNotificationCenter to send received data from the WCSessionDelegate to the SwiftUI view hierarchy. Is there a better way to do that? I have spent a while trying to work out how a WCSessionDelegate class can connect to a binding to a SwiftUI @State property, and cause the UI to update in response to received data, but I haven't made it work.
Are there any newer examples of how to do this? I'm currently only trying to send some simple applicationContext state from the phone to the watch and have some views update to show the latest values.
Thanks, Phil.
We have a watchOS app in TestFlight that is currently crashing on any device running watchOS 11 and up.
I have an older Apple Watch SE that's capped at 10.6.1 that can run the app just fine, but the same exact install on a newer device fails.
This started happening after I updated my MacBook to macOS Sequoia and Xcode to 16.
In order to get the Watch app to run on the 10.6.1 device, I had to change the Swift Optimization Level from -O to -Osize. I already filed Feedback on this (FB15691116).
I've tried so many different things and have come up with nothing to show for it.
When I look in the IPS file, I see that the crash point in my code on the watchOS 11 device is at
KinesiaUOnWatch`@objc SessionDelegator.session(_:activationDidCompleteWith:error:):
0x3a3f48 <+0>: sub sp, sp, #0x60
0x3a3f4c <+4>: stp x26, x25, [sp, #0x10]
0x3a3f50 <+8>: stp x24, x23, [sp, #0x20]
0x3a3f54 <+12>: stp x22, x21, [sp, #0x30]
0x3a3f58 <+16>: stp x20, x19, [sp, #0x40]
0x3a3f5c <+20>: stp x29, x30, [sp, #0x50]
0x3a3f60 <+24>: add x29, sp, #0x50
0x3a3f64 <+28>: mov x19, x4
0x3a3f68 <+32>: mov x23, x2
0x3a3f6c <+36>: mov x22, x0
0x3a3f70 <+40>: mov w0, #0x0 ; =0
0x3a3f74 <+44>: bl 0x89818 ; symbol stub for: type metadata accessor for Swift.MainActor
0x3a3f78 <+48>: mov x24, x0
0x3a3f7c <+52>: adrp x25, 161
0x3a3f80 <+56>: ldr w25, [x25, #0x654]
0x3a3f84 <+60>: mov x20, x0
0x3a3f88 <+64>: bl 0x8980c ; symbol stub for: static Swift.MainActor.shared.getter : Swift.MainActor
0x3a3f8c <+68>: mov x21, x0
0x3a3f90 <+72>: adrp x0, 166
0x3a3f94 <+76>: add x0, x0, #0xc40 ; lazy protocol witness table cache variable for type Swift.MainActor and conformance Swift.MainActor : Swift.Actor in Swift
Incident Identifier: 08E2F667-7965-4C86-B85E-9E2F03EAA963
Beta Identifier: AD390666-55CB-43B3-9B14-BD3E70F456EB
Hardware Model: Watch6,1
Process: KinesiaUOnWatch [389]
Path: /private/var/containers/Bundle/Application/B571E983-F2B0-40EF-9F0D-8C471CAEB3FB/KinesiaUOnWatch.app/KinesiaUOnWatch
Identifier: com.glneurotech.kinesiau.watchapp
Version: 5.1.0 (510271)
AppStoreTools: 16B39
AppVariant: 1:Watch6,1:11
Beta: YES
Code Type: ARM64_32 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.glneurotech.kinesiau.watchapp [464]
Date/Time: 2024-12-03 15:12:51.1876 -0500
Launch Time: 2024-12-03 15:12:50.0000 -0500
OS Version: Watch OS 11.1 (22R585)
Release Type: User
Report Version: 104
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x000000002053dd4c
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [389]
But, as I said, this exact app runs just fine on an older watchOS 10.6.1 device.
Has anyone else encountered something like this, or have any ideas on stuff to try?
Hi, I am new to swift and IOS development, I was developing an app which can be used to communicating between Apple Watch and iPhone.
Something strange occurred when I was trying to observe the status of the message(UserInfo) sent by func transferUserInfo(_ userInfo: [String : Any] = [:]) -> WCSessionUserInfoTransfer.
I was trying to observe isTransferring(a boolean value) in WCSessionUserInfoTransfer which was returned by the function mentioned above, but it seems cannot be updated even if the message queue was empty, it seems to always be True.
Here is my sample code:
let transfer = session.transferUserInfo(message)
if transfer.isTransferring {
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
print("Queued message count: \(self.session.outstandingUserInfoTransfers.count), isTransferring:\(transfer.isTransferring)")
if !transfer.isTransferring {
timer.invalidate()
// irrelevant codes...
}
}
} else {
// other irrelevant codes...
}
Appreciate if anyone can help me out of this problem.
Best wishes.
My Apple Watch after the beta update is not syncing the activities sometimes with Apple Health.
I just completed a 2.5 km walk, and it showed on Apple Health, yes, but it did not affect my daily goals. This is not the first time this has happened; this is just one of the examples that I'm sharing, other than what has been some problems that I am seeing after the beta update.
Additionally, the camera remote app is not working correctly as I cannot see anything on the watch screen like I used to.
We are seeing an issue where sending data using the asynchronous method HKWorkoutSession.sendToRemoteWorkoutSession(data: Data) will never return in some cases (no success nor failure).
This issue is happening for roughly 5% of Workouts started and will stay broken for the whole workout. The other 95% of the workouts, the connection works flawlessly. This happens on both watchOS 10 and 11, and with phones running iOS 17 or 18. The issue is quite random and not reproducible.
Our app has thousands of workouts a day that use the workout session workout data send, with constant messages being send every few seconds.
In some of those 5% cases the "sendToRemoteWorkoutSession" will throw way later, like 30+ minutes later, if the watch app is awake long enough to capture a log of a failure.
Our code uses the same flow as in the sample project:
https://developer.apple.com/documentation/healthkit/workouts_and_activity_rings/building_a_multidevice_workout_app
Here is some sample code, which is pretty simple.
Setup code:
let workoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
workoutSession.delegate = self
activeWorkoutSession?.startMirroringToCompanionDevice { success, error in
print("Mirroring started on companion device: \(success), error: \(error)")
}
workoutSession?.prepare()
then later we send data using the workout session:
do {
print("Will send data")
try await workoutSession.sendToRemoteWorkoutSession(data: data)
print("Successfully sent data") // This nor the error may be called after waiting extensive amounts of time
} catch {
print("Failed to send data, error: \(error)") // This nor the success may be called after waiting extensive amounts of time
}
So far, the only fix is to restart the phone and watch at the same time, which is not a great user experience.
Is anyone else seeing this issue? or know how to fix this issue?
Exploring Live Activity feature for Apple Watch right now and found that it has this default view with "Open on iPhone" button when you tap Live Activity. That button perfectly brings iOS app to foreground as if you tapped iOS's Live Activity.
Is there a way to mimic that behavior from inside Watch app code? From inside WKApplicationDelegate, for example
Tried openSystemURL but it seems lile it's only available for tel or sms links
The app works on iPhone 16 Pro OS18.1 paired AW 10 OS11.0 but when the same iPhone paired with AW OS 11.1, I got the warning below and watch is not responsive
Could not get trait set for device Watch7,9 with version 11.1
really easy to access to you account
Hi, so, we grabbed a couple of nice new watches the other week (Ultra for dad, SE for teenage son). Mostly cool and working together (calls, messages, maps, walkie talkies, etc, etc). All good.
But then son said, "Dad, why can't I see my sleep stuff like you can..?". He was right, it wasn't working. Looking around a bit, it turns out that there are a bunch of things that are turned off or not available when pairing a kids watch with dad's phone.
From the Apple page: "The following features and apps are not available: Medications, respiratory rate, irregular heart rhythm notifications, ECG, AF History, Cycle Tracking, Sleep, Wrist Temperature, Blood Oxygen, Walking Steadiness, Audiobooks, Remote, News, Shortcuts and the double-tap gesture".
Now dev-me reacts to this situation with: "Ok, so let's just build a little standalone sleep app for son's watch. There must be lots of parents out there who would like the same thing". And there are also a bunch of other "family sharing" enabled apps that when you try and use them on kid's phone, say "iPhone requireed", i.e, they don't apparently work with just a watch hooked up to mum or dad's phone.
So before I dive into that kind of project, which seems like an obvious fix path from a dev and a parents' point of view: does anybody know if this from Apple's point of view is a hardware, a software or a legal/age limitation? What's the basic framework/dev/design issue here?
Is it something on the device(s) that prevents sleep data from even being collected on family/kids paired watches? (Therefore don't bother trying to build an app); I assume not becauses it's just a normal SE used by a kid; or
Is it "just" that Apple hasn't wanted to make that available without a kids iPhone too (Therefore you could certainly build a standalone app to do what Apple hasn't wanted to do); or
Netiher 1 nor 2, but Apple won't even allow Sleep data collection for kids for some legal/health data reason (Therefore don't bother trying to build the app).
I have a simple wrapper class around WCSession to allow for easier unit testing. I'm trying to update it to Swift 6 concurrency standards, but running into some issues. One of them is in the sendMessage function (docs here
It takes [String: Any] as a param, and returns them as the reply. Here's my code that calls this:
@discardableResult
public func sendMessage(_ message: [String: Any]) async throws -> [String: Any] {
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<[String: Any], Error>) in
wcSession.sendMessage(message) { response in
continuation.resume(returning: response) // ERROR HERE
} errorHandler: { error in
continuation.resume(throwing: error)
}
}
}
However, I get this error:
Sending 'response' risks causing data races; this is an error in the Swift 6 language mode
Which I think is because Any is not Sendable. I tried casting [String: Any] to [String: any Sendable] but then it says:
Conditional cast from '[String : Any]' to '[String : any Sendable]' always succeeds
Any ideas on how to get this to work?
I started a thread stating the above and apparently I am not the only one to encounter this issue. My Apple Watch is the version 10 and I certainly didn't expect to have this problem. My older Apple Watch did have this same problem initially but after a few updated it stated working. I only tried the consumer beta hoping the issue was resolved. One of the posts today hoped the next update would fix it and I informed him I had installed the beta and it still wasn't working.
I was using iOS 18.1 beta 3 Developer Version in my iPhone 15 Pro max and during the time of apple launch event, Apps in my phone started crashing. When I restarted my iPhone it froze on Apple Logo Screen and kept rebooting from there. I can consider this as a coincidence but i am unable to restore phone to same version or iOS 18 RC.
Now another surprise from last couple of days my airPods Pro are behaving strange, one of the buds doesn’t work many times and even when connected to iPhone it shows just one of the buds but later fixes it automatically.
Now one more surprise since last might my iWatch 8 which is also on beta version has stopped charging.
is anyone else facing the same issue ?
Hi,
I have an app that is performing long-duration audio recording on the Watch and need to communicate with the phone occasionally to:
Request an auth token (login happens on the phone app) when needing to upload a recording.
Occasionally poke the iPhone app to sample the current location (I don't do this on Watch).
Most of the time, both the Watch and iPhone apps would be backgrounded but the Watch app has background audio enabled and is recording, so processing continues.
I'm finding that WatchConnectivity isn't connected to the phone in these cases and cannot send a ping. That is, on the Watch side, WatchConnectivity is not connected to the phone (isReachable==false), and the messages are simply never received on the phone as a result.
I'm not sure how else the apps should communicate this information. How are these scenarios typically handled?
Thank you,
-- B.
Hi everyone,
I have a question regarding the integration of Apple Watch and Vision Pro. Is it possible to connect an Apple Watch to Vision Pro to access health data and display it within Vision Pro applications? If so, could you provide some guidance or point me towards relevant resources or APIs that would help in achieving this?
Thank you in advance for your assistance!
Bought an Apple Watch Ultra 2 and the phone will not recognize it in pairing mode.
both devices have been reset multiple times
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!
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????
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?