Posts

Post marked as solved
4 Replies
572 Views
We have an app that allows users to fetch historical weather data for their selected locations. We are seeing users report failures for specific latitudes and longitudes that come back as WeatherDaemon.WDSClient<WeatherDaemon.WeatherResource>.Errors.responseFailed For example, this request always fails: https://weather-data.apple.com/v3/weather/en-US/33.797/-111.775?timezone=America/Phoenix&dataSets=forecastDaily&dailyStart=2023-12-29T07:00:00Z&dailyEnd=2023-12-31T07:00:00Z&country=US&deviceLanguages=en-US&clientMetadata=<REDACTED> Another example for another location: https://weather-data.apple.com/v3/weather/en-AU/40.717/-74.000?timezone=America/New_York&dataSets=forecastDaily&dailyStart=2023-12-29T05:00:00Z&dailyEnd=2023-12-30T05:00:00Z&country=US&deviceLanguages=en-AU,es-AU&clientMetadata=<REDACTED> Both example involve queries at the end of December 2023. I have filed this as FB13608710 with example code that replicates this as it is being used in our app.
Posted Last updated
.
Post not yet marked as solved
1 Replies
500 Views
It is often the case that offline devices can add duplicate entities that needs to be merged when CloudKit syncs. Consider user-created tags. A user might create a Note, and then tag it with a newly created tag “Family.” On a separate offline device, they might create another note, and create another tag also called ”Family.” On device sync, both duplicate ”Family” tags would need to be identified as duplicates based on their name property, merged to a single entity, and their original relationships consolidated to the single merged Tag. And this needs to happen before the CloudKit sync data is presented to the UI in the main context. With Core Data we have the mechanism to consume relevant store changes described here. These tools allow us to listen for remote change, then process them appropriately (e.g. remove / merge duplicates) and then merge the changes into the app’s main context. This perfectly solves the problem described in the first paragraph above. Apple provides code using this mechanism for deduplicating tags in a sample app. Is there a mechanism to solve this deduplication problem using SwiftData technology without implementing and maintaining a parallel Core Data stack?
Posted Last updated
.
Post marked as solved
1 Replies
286 Views
I have added a strings catalog to my project, and it populates and updates correctly. However, after adding a few "vary as plurals" edits, now the file only renders as a JSON in the editor. So, there's no UI to add languages, for example. The strings catalog still works, and if I manually edit a string it will be compiled into the app, and if I edit a string in my app source, it will update in the JSON file. It's just that the strings catalog UI itself is hidden. If I delete the Localizable file and re-add it, the UI returns. But then if I make my pluralization edits again, the UI disappears and it renders only as JSON. If I drag this xcstrings file into another project it will render with the correct UI, so it seems like a project setting. Is there some setting to re-enable this?
Posted Last updated
.
Post not yet marked as solved
1 Replies
1k Views
I'm using the new interactive widgets to trigger an app intent (tap a widget button, swift data model updates). If I tap the widget while the app is not running, then launch the app, the views (populated by an @Query) will show the correct data. However, if the app is running before tapping the widget button, the app's @Query will not refresh and so the app shows stale data. If I quit the app and relaunch it, or otherwise trigger a refresh of the query, then the view will update. Does anyone have a way to get the app to notice when an App Intent has changed Swift Data values outside of the main app? edit: Reported as FB13278891
Posted Last updated
.
Post not yet marked as solved
4 Replies
2k Views
In the WWDC 23 lounge, this exchange with @Dave N (Apple) indicated that using ModelActor is the right way to do background work using swift concurrency. https://developer.apple.com/forums/thread/731338 I've figured out how to use this approach to do background work using swift concurrency (inside a Task), and example code is below for those who find it useful, however I'm not seeing view updates when the background work is complete. There seems to be no way to trigger a view update based on a @Query when inserts happen in a ModelActor’s context. However, if a delete happens on the ModelActor context, this DOES trigger a view redraw. I believe this is a bug because I expect the behavior to be the same for inserts and deletes. I've submitted this as Feedback FB12689036. Below is a minimal project which is the default SwiftData template, where the Add and Delete buttons point to a ModelActor instead of the main view ModelContext. You will see that using the addItem function in the ModelActor does not trigger a UI update in ContentView. However if you relaunch the app the added Item will be present. In contrast, using the delete function on the ModelActor context does trigger an immediate view update in ContentView. If this is intended behavior, we need a way to merge changes from background contexts, similar to what is described in the Core Data document “Loading and Displaying a Large Data Feed”: https://developer.apple.com/documentation/swiftui/loading_and_displaying_a_large_data_feed In Core Data we have automaticallyMergesChangesFromParent and mergeChanges(fromContextDidSave:) to do this manually. There seems to be no equivalent for Swift Data. If anyone has solved this problem of merging changes from other contexts, or can confirm that this is a bug, please let me know. import SwiftUI import SwiftData struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] @State private var simpleModelActor: SimpleModelActor! var body: some View { NavigationView { List { ForEach(items) { item in NavigationLink { Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") } label: { Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) } } .onDelete(perform: deleteItems) } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { EditButton() } ToolbarItem { Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } Text("Select an item") } .onAppear { simpleModelActor = SimpleModelActor(modelContainer: modelContext.container) } } private func addItem() { Task { await simpleModelActor.addItem() } } private func deleteItems(offsets: IndexSet) { Task { for index in offsets { await simpleModelActor.delete(itemWithID: items[index].objectID) } } } } import Foundation import SwiftData final actor SimpleModelActor: ModelActor { let executor: any ModelExecutor init(modelContainer: ModelContainer) { let modelContext = ModelContext(modelContainer) executor = DefaultModelExecutor(context: modelContext) } func addItem() { let newItem = Item(timestamp: Date()) context.insert(newItem) try! context.save() // this does not impact a re-display by the @Query in ContentView. I would have expected it to cause a view redraw. } func delete(itemWithID itemID: Item.ID) { let item = context.object(with: itemID) context.delete(object: item) // this DOES cause a view redraw in ContentView. It triggers an update by @Query. // try! context.save() // this makes do difference to view redraw behavior. } }
Posted Last updated
.
Post marked as solved
3 Replies
1.2k Views
Is everyone else seeing that ImageRenderer is unable to render the contents of a ScrollView? When using the ImageRenderer class introduced in iOS16, the render will not contain any Views that are inside of a ScrollView. The ScrollView contents within the rendered image will be blank. This is surprising to me since I would think ScrollView use is pretty common, and so ImageRenderer would have been tested with it. Is there any documentation out there explaining what types of Views or scenarios will cause a failure of ImageRenderer? To reproduce, run the ContentView below inside an iOS SwiftUI project. Tap the “Render this View to Image” button. Expected results: the green-bordered Image should show a screenshot of the entire View, including the contents of the ScrollView (“Text inside a ScrollView”). Actual results: the rendered screenshot shows the region of the ScrollView, but there are no contents shown. It is blank. import SwiftUI struct ContentView: View {     @State private var renderedImage: UIImage?     var body: some View {         VStack {             Button("Render this View to Image") {                 renderedImage = ImageRenderer(content: self).uiImage!             }             VStack {                 Text("rendered image")                 Image(uiImage: renderedImage ?? UIImage(systemName: "xmark")!)                     .resizable()                     .aspectRatio(contentMode: .fit)             }             .frame(height: 200, alignment: .center)             .padding(5).border(.green, width: 5)             VStack {                 Text("A ScrollView is shown below")                 ScrollView {                     VStack {                         Text("Text inside a ScrollView")                         Text("Text inside a ScrollView")                         Text("Text inside a ScrollView")                         Text("Text inside a ScrollView")                     }                     .fixedSize()                 }             }             .padding(5).border(.red, width: 5)             .frame(height: 100)         }         .padding()     } }
Posted Last updated
.
Post not yet marked as solved
0 Replies
740 Views
A lost feature from Core Data seems to be the ability to enforce a uniqueness constraint based on multiple attributes. For example, with Core Data one could have a constraint for “firstName,lastName” and only the combination of the same firstName and lastName was enforced to be unique. In SwiftData there seems to be no way to achieve this with the @Attribute(.unique) macro. It seems that the only solution would be to create a name-mangled composite attribute and automatically update that on insert, but this should be a feature of SwiftData. If I'm missing something please let me know. The relevant entity attribute from an xcdatamodeld file is: <uniquenessConstraints> <uniquenessConstraint> <constraint value="firstName"/> <constraint value="lastName"/> </uniquenessConstraint> </uniquenessConstraints> I filed this as feedback FB12385087
Posted Last updated
.
Post marked as solved
1 Replies
698 Views
When using the new ImageRenderer introduced in iOS16, the Picker control does not render correctly. I have filed it as FB11994261. Does anyone have a workaround for this which still uses ImageRenderer? As an aside: the reason I need to use ImageRenderer is that in my current project I'm trying to render a large (>2730 point tall) view to an image for the user to share. Solutions using UIGraphicsImageRenderer will silently fail with a clear/black screen when trying to render views with any dimension >2730 pt. ImageRenderer is the only view capturing technique that I've found which will successfully render a View greater than this threshold. To replicate: add a Picker to a view. Attempt to render that view using ImageRenderer. The resulting image will not match what is displayed on screen. Step-by-step: Create a default SwiftUI project in Xcode. Replace the main content view with this code: struct ContentView: View { @State private var renderedImage: UIImage? @State private var showSheet = false @State private var picked: Int = 0 var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundColor(.accentColor) Text("Picked \(picked)") Picker("Pick a number", selection: $picked) { ForEach([0,1,2,3], id:\.self) { number in Text("\(number)") } } .pickerStyle(.segmented) } .padding() .sheet(isPresented: $showSheet) { if let renderedImage { Image(uiImage: renderedImage) } } .task { Task { renderedImage = ImageRenderer(content: self).uiImage! showSheet = true } } } } Expected result: on loading the view, ImageRenderer captures the current state of ContentView, a sheet opens, and the screenshot is display, including the Picker UI element. Actual results: The screenshot is presented in a sheet, but the Picker element is replaced by a yellow field with a red slash-circle in the center. Attached are images of the actual ContentView, and the sheet displaying the failed render.
Posted Last updated
.
Post not yet marked as solved
1 Replies
483 Views
I've got an app live in the App Store, and a second app that I'm developing right now. In TestFlight (only!), when I display a manageSubscriptionsSheet in the new app I'm seeing the TestFlight subscription status for the other app. If I run the new app on the same device in debug mode (connected to my laptop), I get the correct manageSubscriptionsSheet displayed. In both environments (Debug and TestFlight) I see the correct purchase sheet showing the right subscription options. It is only the manageSubscriptionsSheet that is incorrect. Both the new app and the old app (the one showing up in manageSubscriptionsSheet) have TestFlight builds on my phone. Of course, as I type this up I reinstall the TestFlight build (after seeing the correct manageSubscriptionsSheet in the debug build) and now I'm seeing the correct manageSubscriptionsSheet. Is this something I'm doing wrong in my app, or is this a known issue with TestFlight apps? I know it's expected that the entitlements are different on TestFlight than in debug, but would manageSubscriptionsSheet show something different than a purchase sheet?
Posted Last updated
.
Post marked as solved
1 Replies
1.4k Views
If an app has a subscription (or IAP) with family sharing enabled, will beta testers in TestFlight be able to test that functionality? I’m working on an app that includes family sharing for IAP, and I highlight family sharing in the paywall. However, my testers are reporting that they do not get the entitlement when a family member purchases the IAP. I’m trying to determine if I’m making an error in my code. The family sharing tech talk indicates that no special treatment is required in the app, so I’m wondering if this is just a limitation of the TestFlight store environment.
Posted Last updated
.