I would like to deploy a WatchOS app without the companion iOS app.
Here's my situation:
I have created a WatchOS app with a companion iOS app
The watch app supports running without the companion app
The watch app is fully functional, and in fact the iOS companion is not built yet
Is it possible to submit the WatchOS app without the companion iOS app with the option to submit the iOS companion at a later date?
I would like to avoid having to create a new watch only app because once I release the iOS companion app, the old watch app would be considered a different app.
If it is possible, some guidance on how to achieve this submission would be appreciated.
Post
Replies
Boosts
Views
Activity
I have a question regarding the following SwiftUI setup using @Model.
I have a parent view which queries the model, extracts the first element, and passes that to child views.
My issue is that when I make changes to properties marked as @Transient in the model, the child views do not re-render.
Parent
struct ParentView: View {
@Query private var myData: [MyData]
var body: some View {
if let data = myData.first {
TabView {
Overview(data: data)
Details(data: data)
}
.tabViewStyle(.verticalPage(transitionStyle: .identity))
} else {
ContentUnavailableView {
Label {
Text(verbatim: "Failed to load app content")
} icon: {
Image(systemName: "xmark")
}
}
}
}
}
Child
struct ChildView: View {
var data: WaterData
var body: some View {
Text("\(data.progress)")
}
}
If I query the model in the child view directly, then changes correctly propagate to the child view.
What am I doing wrong? Is there a property with which I can mark the var data in the child view to let SwiftUI know it should observe changes?
The underlying data in the model is marked @Transient because the source of truth is synched from Apple Health:
@Transient public var samples: [HKQuantitySample] = []
I should also note that the child view renders based on computed properties, such as:
public var progress: Double {
let todaysRecords = samples.filter {
Calendar.autoupdatingCurrent.isDateInToday($0.endDate)
}
let sum = todaysRecords.reduce(0, { sum, record in
sum + record.quantity.doubleValue(for: .literUnit(with: .milli))
})
return sum
}
Apologies for the messy post. I'm not sure which part of this setup breaks the UI updates. I could imagine having a transient property and using computed vars in the UI prevent SwiftUI from knowing about the changes. On the other hand, if I do the @Query inside the child view, it works, despite the transient prop and computer vars.
I'm running into an issue with using .topBarTrailing placement for a toolbar item. The app fails to launch (crashes) with this placement. The following view works fine with any other placement (other than .topBarLeading).
What am I doing wrong?
var body: some View {
NavigationStack {
Text("Overview")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
// noop
} label: {
Label("Add", systemImage: "plus")
}
}
}
.navigationTitle("Overview")
.navigationBarTitleDisplayMode(.inline)
}
}
I've opted to use .confirmationAction as a workaround, which works fine. It also positions the toolbar item in the same place on the view as the .topBarTrailing placement would.
I'm using Xcode version 15.0 and targeting WatchOS 10.
Verbose error output when trying to run the app in the simulator:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Layout requested for visible navigation bar, <PUICStackedNavigationBar: 0x100e1e8d0; baseClass = UINavigationBar; frame = (0 0; 198 60); opaque = NO; autoresize = W; layer = <CALayer: 0x60000027c280>> delegate=0x101877800 standardAppearance=0x60000261cc60, when the top item belongs to a different navigation bar. topItem = <UINavigationItem: 0x100f11230> title='Overview' style=navigator, navigation bar = <PUICStackedNavigationBar: 0x100f22a80; baseClass = UINavigationBar; frame = (0 0; 198 60); opaque = NO; autoresize = W; layer = <CALayer: 0x6000002887c0>> delegate=0x101069600 standardAppearance=0x60000261f3c0, possibly from a client attempt to nest wrapped navigation controllers.'
Isn't it possible to use the same widgetkit extension for both iOS and WatchOS?
In the WWDC's BackyarBirds project from Apple, I can see the widget extension is added to both the multiplatform (including iOS) and the watch targets. I can also see that the SwiftUI code even uses macros to check which platform the code is running on.
However, I'm having several issues with adding the same extension to both targets. When I add it to the watch target, I get a build error that I'm trying to embed an application that also builds for iOS and that is not allowed for the watch app.
Not sure if any code here is helpful, but this is my widget UI code:
struct AkvaWidget: Widget {
private let kind = "Akva Widget"
var families: [WidgetFamily] {
#if os(iOS)
return [.accessoryCircular, .accessoryRectangular, .systemSmall]
#elseif os(watchOS)
return [.accessoryCircular, .accessoryRectangular, .accessoryInline, .accessoryCorner]
#endif
}
var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: AkvaSnapshotTimelineProvider()
) { entry in
AkvaWidgetView(entry: entry)
}
.configurationDisplayName("Akva")
.description("Keep track of your water intake.")
.supportedFamilies(families)
}
}
When I update a variable inside my model that is marked @Transient, my view does not update with this change. Is this normal? If I update a non-transient variable inside the model at the same time that I update the transient one, then both changes are propagated to my view.
Here is an example of the model:
@Model public class WaterData {
public var target: Double = 3000
@Transient public var samples: [HKQuantitySample] = []
}
Updating samples only does not propagate to my view.
Is it possible to either query a single instance or instruct SwiftData that there should only be a single instance in my model?
I have the following basic structure in a lot of my views to query the first element and use it to display data. There is always only one instance, it is by design.
@Query private var waterData: [WaterData]
var body: some View {
if let data = waterData.first {
EmptyView()
} else {
ContentUnavailableView("Content unavailable", systemImage: "xmark.circle")
}
}
Is there a way to reduce the boilerplate of if let data = ... in each view?
I'm trying to build a WatchOS 9 corner complication with WidgetKit (using Xcode 14 beta 3).
I'd like my complication to look like the battery one in the bottom right corner. Here's my view:
struct HydrationProgressCorner: View {
var entry: Provider.Entry
var body: some View {
Text("\((100 * entry.progress / entry.target).rounded(.towardZero).formatted())%")
.widgetLabel {
ProgressView(value: entry.progress, total: entry.target)
.tint(.blue)
.widgetAccentable()
}
}
}
How can I get my text to follow the curvature of the watch face?
Even though beta 3 change logs claim there's a fix for the preview error Type 'WidgetEntryPointFactory' does not conform to 'NonUIEntryPoint', it is still an issue for me consistently.
The previously accepted workaround for this issue also doesn't work for me anymore, which was: mv ~/Library/Developer/CoreSimulator/Caches/dyld{,_old} ; sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService.
What's a developer to do to get the preview working again? 🤓
Full diagnostics message I get:
RemoteHumanReadableError: Type 'WidgetEntryPointFactory' does not conform to `NonUIEntryPoint` (in framework at path /System/Library/PrivateFrameworks/ChronoCore.framework/Support/WidgetPreviewsExtensionAgent.bundle)
==================================
| MessageSendFailure: Message send failure for <ServiceMessage 50: update>
Xcode: Version 14.0 beta 3 (14A5270f)