hi,
did you check your account preferences in Xcode? that's where you can log in from within Xcode.
Xcode --> Preferences ... --> Accounts
hope that helps,
DMG
Post
Replies
Boosts
Views
Activity
-- the full statement in the 14beta3 notes reads: "Fixed: The diagram view has been removed from the Core Data data model editor in Xcode 14."
hi,
just to think ahead ... Xcode 14 will reach a release version in, maybe, September. upgrades from there on (14.1, 14.2, ...) in the first few months will still run on MacOS 12 (Monterey); but expect that early next year, Xcode 14.3 or whatever it is by then will undoubtedly require MacOS 13 (Ventura). your 2015 MacBook Pro will not run Ventura.
for reference: Xcode 13 ran on MacOS 11 (Big Sur) up through version 13.2.1; version 13.3, released on March 14, 2022, was the first to require MacOS 12 (Monterey).
hope that helps,
DMG
hi,
@Environment is only available (well, only makes sense) in SwiftUI views. @Environment(\.managedObjectContext) var moc specifically picks out the managed object context in a view's SwiftUI environment -- that context is often created and placed into the SwiftUI environment in the main-level App file.
if you are in some other non-view code, say like a view model object, you would need a direct reference to the managed object context.
example: in Apple's default Core Data project template, it creates a shared global singleton PersistenceController object, and injects its managed object context into the main level ContentView (which is then shared in subviews) by writing
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
if this is your situation, you can reference the managed object context anywhere in your code (within view or not) by writing
let moc = persistenceController.shared.container.viewContext
hope that helps,
DMG
hi,
if the issue is about loading pre-defined data on a device (independent of the CloudKit not supporting uniquing of objects on its own), any strategy of "if the store is empty, then load up all the pre-defined data" will fail, because you cannot count on when or if the cloud is available -- there's no guaranteed way to properly make the decision "should i load the pre-defined data."
your DTS answer is probably the best -- examine the history tracking. this document from Apple is probably the relevant one.
a second possible strategy is to use two different configurations in your Core Data model. all pre-defined data goes into a local store that is not synched to the cloud; but all user-defined/user-modified data goes into a cloud store that is synched with the cloud.
you can check these references:
on how to manage multiple stores (Apple Developer Documentation)
on how to implement links between the local and cloud stores (Apple Developer Documentation).
hope that helps,
DMG
hi,
Zimmie is right -- set the Custom Class to [Int]; but three quick things:
for your transformable attribute, you will also need to set the Transformer to be NSSecureUnarchiveFromDataTransformerName; you'll otherwise probably get a warning about this at some point.
remember that the values you store in a transformable attribute will not be searchable using any sort of fetch request.
if you really are a hopeless newbie, i would suggest setting the CodeGen to Class Definition and let Xcode generate the Core Data classes for you, especially if you Core Data model has relationships.
hope that helps,
DMG
hi,
since I am the one who recommended this, I feel it's probably my job to clean this up.
bottom line: I forgot you were working with string attributes in Core Data and they are optional.
option 1: just force-unwrap the title. use this (assuming you always assign a title to a Recipe):
ForEach(recipes.filter({$0.title!.contains(searchingFor)})) { recipe in ...
option 2: If you don't like force-unwrapping, then do this.
first, add an extension to the Recipe class to massage the title a little bit:
extension Recipe {
var niceTitle: String { title ?? "" }
}
and then rewrite you ForEach construct as
ForEach(recipes.filter({$0.niceTitle.contains(searchingFor)})) { recipe in ...
hope that helps,
DMG
@FetchRequest is a SwiftUI construct ...
hi,
@FetchRequest in a SwiftUI construct to be included within a View that gives you access to an array of (what are essentially) Recipe objects for use within the View. it's not something where you procedurally say "give me a fetch of objects."
if your function is defined within a View, then the View should have a local variable that keeps up to date with all the recipes:
@FetchRequest(entity: Recipe.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Recipe.date, ascending: false)
])
var recipes: FetchedResults<Recipe>
you can search through these recipes directly with
func searchResults(searchingFor: String) -> [Recipe] {
recipes.filter({ $0.title.contains(searchingFor) })
}
otherwise, you should execute a direct Core Data fetch, setting up an NSFetchRequest with appropriate NSSortDescriptors and NSPredicates to query Core Data.
hope that helps,
DMG
hi,
first, you might start with something simple and straightforward: Paul Hudson's 100 Days of SwiftUI, specifically the BookWorm project which is Day 53 of the curriculum. look for these links:
https://www.hackingwithswift.com/100/swiftui
https://www.hackingwithswift.com/100/swiftui/53
the only thing missing from your list of requests is the ability to delete an item from within the detail view.
it's exactly because of this omission that i got started on my own project with Xcode 11 called ShoppingList (and then its iterations for ShoppingList14 with iOS 14 and Xcode 12, and now ShoppingList15 with iOS 15 and Xcode 13) that implemented (!) deletion from within the detail view. i've tried to comment extensively throughout the code to indicate issues i found in development, especially those that involve my eventual solution of many "did not update" issues most people run into.
you can find my current development at the following link, and as long as you are on the master branch (based on using @FetchRequests), you should find plenty of code to work with. (the MVVM branch in that project is not quite there yet, which does not use @FetchRequests.)
https://github.com/delawaremathguy/ShoppingList15
in answer to your question
What is the best way to do a detail view with a list? Iterate over the master object's list of detail items or use a fetch request with a predicate for the master?
i think you are better off "passing in" a Core Data object to a detail view (often as an @ObservedObject, despite the fact that in-detail-view deletion has a little bit of a twist to it) than making a fetch request.
hope that helps,
DMG
hi,
when you say
I have created an observable class that contains a published property of type [Event] to store a Events in an array ...
what you really mean by store is that you have an in-memory array of type [Event] and you have appended a new Event to that in-memory array; but you have not persisted the data outside of your app.
common solutions to persist data across launches (of an iOS application) include:
writing data to a file in the user's Document's directory
writing data to UserDefaults (not ideal, and certainly not scalable)
saving data to Core Data (perhaps with a CloudKit component)
writing data directly to CloudKit
you will need to read up on these solutions. i highly recommend Paul Hudson's 100 Days of SwiftUI course (https://www.hackingwithswift.com/100/swiftui), or 100 Days of Swift (if you are using UIKit), where several of these methods are described.
good luck,
DMG
forgot to mention: i have posted some code where you can see my evolution in thinking on these ideas.
hi,
i like your question:
Which of the two options above makes more sense for an MVVM app?
i have used one, both, sometimes neither, and sometimes variations, and i have not really liked any enough to say that "that's what i'd do" the next time i work on an App.
some thoughts:
(1) my bottom-line goal is to be sure that no ShiftUI View should be in the business of really knowing that the (class) objects it works with are Core Data objects. for example: i don't ever want a View executing code that uses the managed object context to create a new object or delete an existing object. so some form of VM is needed, whether it be a global singleton or a locally-created @StateObject on a per-view basis.
(2) yet a second goal is to avoid building a MVM = "Massive View Model." because you are using Core Data objects, i think your first option might be better, because you have relationships and fetch requests to manage. i think those are better done and centralized by some object that has a global view of the database.
(3) remember that with Core Data relationships in play, there are some subtleties that should be handled in a VM. for example: when updating an existing CarService, perhaps in a detail view, the VM will know that it must manually execute carService.car?.objectWillChange.send() so that the associate Car record should be treated as having changed (changing a field in CarService does not do this for you), because you probably have some Views in your app that hold an @ObservedObject Car object reference.
(4) you have the decision as to whether you want to use @FetchRequests directly to deliver an array of objects to a view, or have a VM use its own NSFetchedResultsController objects to deliver that array.
other than for simple views, such as a List, i have usually chosen the latter. however, even then, i've started to dislike having to begin every SwiftUI view file with the usual litany of property wrappers, one of which is a @FetchRequest that's repeated verbatim in multiple views throughout the app.
(5) i usually add a few convenience extensions to the Core Data classes to do simple things, such as nil-coalesce optional fields of the Core Data record, convert between Int and Int32/Int16, or provide a computed variable (one that's especially useful is to return a properly typed Set or Array of CarService objects for a given Car).
but i have found it useful to also include creation and deletion functions in that extension as class functions on the Core Data class itself.
so instead of writing a "let newService = CarService(context: contact) ..." sequence that defaults and hooks up the new service with an existing Car, i will write a single statement "let newService = CarService.add(to: car, ...)" and leave the details to the CarService class to do the creation, default all the fields, and hook the service to the Car. this simplifies the call site, and is especially useful if there are several points in your app where you add services to a car.
hope some of this helps,
DMG
hi,
i would think your easiest solution is to use the built-in linkage between Core Data and CloudKit using NSPersistentCloudKitContainer.
this takes care of all of your concerns: your data is stored locally in Core Data and mirrored to/from the cloud for you. everything works fine when offline or not signed in to iCloud.
just remember that in this design, the Cloud becomes the "source of truth" for your data. modify data on one device; it is synced with iCloud; and then the data is synced with your other devices, where their local Code Data store is synced to match what's in the cloud.
there would be no option for the user here to "choose" which data they want to use, as you say, "the backed-up data or the new data." once your device is online and signed into iCloud, your on-device data will be synced to be a mirror of what's in the cloud.
hope that helps,
DMG
Drew,
some quick comments, since i cannot see your code (if it were available on GitHub, that might be of interest).
in the Core Data model editor, find the attribute date in whatever entity you are working with and check that its Type shows as Date. if it shows as String there, just click on the String value and choose Date as the Type from the pull-down menu. (you may already have done this, but i cannot tell from your question.) so Core Data knows the values are dates, and sorting with ascending: false will give most recent dates first, oldest dates last.
Xcode will give your app/program access to the date attribute as a Swift variable var date: Date? defined on the class object for your entity. notice that this is an optional Date in Swift, so treat it accordingly when reading it.
all your existing code that reads from the date attribute or assigns to the date attribute should be working with real Date objects in Swift. you cannot assign a String to a Date? object; but really, other than for display purposes, you should not be working with string values of dates in your code anyway, since constantly translating back and forth between Strings and Dates is hard.
hope that helps,
DMG