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.1k
Aug ’22