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?