Post

Replies

Boosts

Views

Activity

Show Core Data fetch results in WidgetKit
I'll say up front that this code was working in Xcode 14 beta 3, but now in beta 4, it's not. I'm using the default Core Data code that comes when you select the Use Core Data checkbox when creating a new project, except I changed the sample item to a sample Pokemon. import CoreData struct PersistenceController { static let shared = PersistenceController() let container: NSPersistentContainer static var preview: PersistenceController = { let result = PersistenceController(inMemory: true) let viewContext = result.container.viewContext let samplePokemon = Pokemon(context: viewContext) samplePokemon.id = 1 samplePokemon.name = "bulbasaur" samplePokemon.types = ["grass", "poison"] samplePokemon.hp = 45 samplePokemon.attack = 49 samplePokemon.defense = 49 samplePokemon.specialAttack = 65 samplePokemon.specialDefense = 65 samplePokemon.speed = 45 samplePokemon.sprite = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png") samplePokemon.shiny = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/1.png") samplePokemon.favorite = false do { try viewContext.save() } catch { let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } return result }() init(inMemory: Bool = false) { container = NSPersistentContainer(name: "Poke") if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) container.viewContext.automaticallyMergesChangesFromParent = true } } I have the PersistenceController added to my app and my widget target, so they can both access it. I also added my Core Data model to both targets. Now, here's my widget code, and remember this was already working in beta 3. import WidgetKit import SwiftUI import CoreData struct Provider: TimelineProvider { let managedObjectContext: NSManagedObjectContext var randomPokemon: Pokemon { let fetchRequest: NSFetchRequest<Pokemon> = Pokemon.fetchRequest() var results: [Pokemon] = [] do { results = try managedObjectContext.fetch(fetchRequest) } catch { print("Couldn't fetch: \(error)") } print(results) if let randomPokemon = results.randomElement() { return randomPokemon } return SamplePokemon.samplePokemon() } init(context: NSManagedObjectContext) { managedObjectContext = context } func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date(), pokemon: SamplePokemon.samplePokemon()) } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date(), pokemon: randomPokemon) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] // Generate a timeline consisting of five entries an hour apart, starting from the current date. let currentDate = Date() for hourOffset in 0 ..< 5 { let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! var entry = SimpleEntry(date: entryDate, pokemon: randomPokemon) entry = SimpleEntry(date: entryDate, pokemon: randomPokemon) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } } struct SimpleEntry: TimelineEntry { let date: Date let pokemon: Pokemon } struct RandomPokemonEntryView : View { @Environment(\.widgetFamily) var widgetSize var entry: Provider.Entry var body: some View { switch widgetSize { case .systemSmall: WidgetPokemon(pokemon: entry.pokemon, widgetSize: .small) case .systemMedium: WidgetPokemon(pokemon: entry.pokemon, widgetSize: .medium) case .systemLarge: WidgetPokemon(pokemon: entry.pokemon, widgetSize: .large) default: WidgetPokemon(pokemon: entry.pokemon, widgetSize: .large) } } } @main struct RandomPokemon: Widget { let kind: String = "RandomPokemon" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider(context: PersistenceController.shared.container.viewContext)) { entry in RandomPokemonEntryView(entry: entry) } .configurationDisplayName("Random Pokemon") .description("Shows a random Pokemon.") } } struct RandomPokemon_Previews: PreviewProvider { static var previews: some View { Group { RandomPokemonEntryView(entry: SimpleEntry(date: Date(), pokemon: SamplePokemon.samplePokemon())) .previewContext(WidgetPreviewContext(family: .systemSmall)) RandomPokemonEntryView(entry: SimpleEntry(date: Date(), pokemon: SamplePokemon.samplePokemon())) .previewContext(WidgetPreviewContext(family: .systemMedium)) RandomPokemonEntryView(entry: SimpleEntry(date: Date(), pokemon: SamplePokemon.samplePokemon())) .previewContext(WidgetPreviewContext(family: .systemLarge)) } } } The part to focus on is right at the top, that randomPokemon computed property. The thing is, again, this exact code was working before. That's why I'm so frustrated that it just refuses to work now, no matter what I do. In my searching I've seen one suggestion is to create an App Group and share the Core Data stack in there. But I've seen other solutions that don't say that. And obviously, again, mine was already working without an App Group, so yeah... Now my questions. Was there a bug in Xcode 14 beta 3 that allowed me to access the same Core Data store without an App Group? Or is there now a bug in Xcode 14 beta 4 that is just breaking my code somehow? And either way, are there any changes I can make to my code so I can get my data to fetch for my widget?
4
0
1.3k
Sep ’22
How to show value totals in BarMark Charts
In the WWDC22 video "Design an effective chart", at timestamp 1:22, it shows 4 screenshots with different charts. In the 3rd screenshot from left, it shows the most sold pancake as well as sales of all the different types of pancake. The number of pancakes sold is pinned to the end of each bar, like 278, 247, and so on. How do you show the number like that?
1
0
1.1k
Jul ’22