UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Inserting and fetching data in Core Date

Forums > Swift

Having completed the 7/7 tutorial (https://www.youtube.com/watch?v=y1oWprQqLJY) for Core Data I am at a loss as to how to structure my data. I am looking for a relationship model, but cannot amend a single specific piece of data. Each month I record customers, but while I can add customers I cannot figure out how to organise data just by month and year ?

extension Record {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Record> {
        return NSFetchRequest<Record>(entityName: "Record")
    }

    @NSManaged public var month: String?
    @NSManaged public var year: String?
    @NSManaged public var customer: NSSet?

//yea, I know it’s a String value, there is a reason (ditto below)
    public var wrappedMonth: String {
        month ?? String
    }

    public var wrappedYear: String {
        year ?? String
    }

//I could not get this Set sorting method to work, but I’m not use SwiftUI either
//    public var customerArray: [Customer] {
//          if let set = customer as? Set<Customer> {
//            return set.sorted(by: { 
//      $0.wrappedYear < $1.wrappedYear 
//  } )
//          }
//            return []
//     }

//    public var customerArray: [Customer] {
//        let set = customer as? Set<Customer> ?? []
//
//        return set.sorted {
//            $0.wrappedYear < $1.wrappedYear
//        }
//    }
}

// MARK: Generated accessors for customer
extension Record {

    @objc(addCustomerObject:)
    @NSManaged public func addToCustomer(_ value: Customer)

    @objc(removeCustomerObject:)
    @NSManaged public func removeFromCustomer(_ value: Customer)

    @objc(addCustomer:)
    @NSManaged public func addToCustomer(_ values: NSSet)

    @objc(removeCustomer:)
    @NSManaged public func removeFromCustomer(_ values: NSSet)
}
extension Customer {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Customer> {
        return NSFetchRequest<Customer>(entityName: "Customer")
    }

    @NSManaged public var Customer: String?
    @NSManaged public var age: Int16
    @NSManaged public var gender: String
    @NSManaged public var country: String?

    public var wrappedCustomer: String {
        Customer ?? "Unknown Customer"
    }

    public var wrappedAge: Int16 {
        age ?? 0
    }

    public var wrappedGender: String {
        gender ?? String
    }

    public var wrappedCountry: String {
        country ?? String
    }
}

I use the following to add the data in, and can append to create nested results, but it is editing those that I have couble with:

let userEntryRecord = NSEntityDescription.entity(forEntityName: “Record”, in: managedContext)!
let userEntryCustomer = NSEntityDescription.entity(forEntityName: “Customer”, in: managedContext)!
let dataRecord = NSManagedObject(entity: userEntryRecord, insertInto: managedContext) as? Record
let dataCustomer = NSManagedObject(entity: userEntryCustomer, insertInto: managedContext) as? Customer

    dataRecord?.year = “2021”
    dataRecord?.month = “March”
    dataRecord?.addToCustomer(dataCustomer ?? Customer())

    dataCustomer?.age = 20
    dataCustomer?.gender = “male”
    dataCustomer?.country = “UK

    //save method

With this system the customer will inset into records, but I can't figure out what the predicate would be to extract the specific data I need i.e. Year 2022, Month January, for people aged 20-30yrs.

It also seems to hard code the Yerar and Month data, I can't seem to be able to create any new date Records.

I would like to avoid predicates as they're so un-Swift like, and often cast as the Xcode generated Classes, is it possible to sort in a more Swift like manner, or does that negate the power of Core Data ?

3      

you'll get used to predicates dont worry and when you do, you can re-use a lot of them

have you started trying to do a fetch request already? if so then do you have that code to show us?

if not then I can guide on how to create it.

Richard

3      

Hello rlong405, many thanks for your assistance. I presently do not use any predicates. With a combination of functions to commit the data through parameters I can achieve the following output in the LLDB:

Optional(Relationship ‘customer’ on managed object (0x6000023232f0) <Record: 0x6000023232f0> (entity: Record; id: 0x833151749c5958de <x-coredata://0CCE7739-3CB5-4606-8D73-ECD6F140B162/Record/p29>; data: {
    customer =     (
        "0x833151749d2558dc <x-coredata://0CCE7739-3CB5-4606-8D73-ECD6F140B162/Customer/p66>",
        "0x833151749d2958dc <x-coredata://0CCE7739-3CB5-4606-8D73-ECD6F140B162/Customer/p65>"
    );
    year = 2021;
    month = march;
}) with objects {(
    <Customer: 0x600002322f30> (entity: Customer; id: 0x833151749d2558dc <x-coredata://0CCE7739-3CB5-4606-8D73-ECD6F140B162/Customer/p66>; data: {
    age = 20;
    gender = male;
    country = UK;
    register = "0x833151749c5958de <x-coredata://0CCE7739-3CB5-4606-8D73-ECD6F140B162/Record/p29>";
}),
    <Customer: 0x600002323340> (entity: Customer; id: 0x833151749d2958dc <x-coredata://0CCE7739-3CB5-4606-8D73-ECD6F140B162/Customer/p65>; data: {
    age = 25;
    gender = male;
    country = UK;
    register = "0x833151749c5958de <x-coredata://0CCE7739-3CB5-4606-8D73-ECD6F140B162/Record/p29>";
})
)})

Beginning with something basic I used to following to try to segregate by just the year Record:

fetchRequest.predicate = NSPredicate(format: "year = %@", "2021")

    do {
        let result = try managedContext.fetch(fetchRequest)

        let objectToUpdate = result[0] as? Record
        objectToUpdate?.year = "2022"

           //save data

    } catch {
        let fetchError = error as NSError
        print(fetchError)
    }

I don't even know if I'm comitting the data correctly, or how Core Data stores this data? I am used to working with Plists, and I was able to structure persistent data in a nested fashion. Is all of the data in one big soup and the output then structured, does comitting affect retrieval in that light, or is the predicate fetch request the means of structuring the data ?

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

The way you've created your entities and records isn't overly familiar to me. I tend to create my entities in the GUI and then create a managedObjectSublass. This is all done by me up front. The run time code then adds, fetches and deletes records from those already created entities.

I think you may be right as I don't see where you are committing your changes when you create or change your data.

Try adding

try? managedContext.save()

in the 2 places where you have commented //save data

Does that save the changes?

3      

My implementation is dynamic, with the code deciding what data to save for its own reference, rather than data inputted by a user. I know that doesn't mesh with my example, but the issue for me is CRUD, not the the type of data and how it's inputted.

I begin with no records, so they're created, and removed, as necessary. The logic that I provided only focused on the data entry and retrival methods that I currently employ, the save method varnished away as my post is already code heavy. But the save method works as I can reteieve the comitted data.

Your approach will be the same as what I need, only functions would juggle the data as needed. So to go back to the beginning, how would you commit three new Customer data entires into an existing Record, then create a new Record for another group of four Customers, and then edit the third Customer entry (e.g. aged 58) in one of the records dated e.g. April 2021 ?

3      

my apologies - I thought you'd ommitted the save element and that was the issue. my bad

Currently on my phone as its getting late here so I wont attempt to provide code from memory (I'll do that tomorrow if interested)

so for now Ill answer your back to the beginning question conceptually.

what I would do is as follows

to commit 3 new customer entries into a record, first I would fetch the Record that I want to add the customers to. lets assume April 2021. As you have month and year as seperate properties then you need to use a compound predicate of year = 2021 and month = April. Ive got lots of examples of these so I can provide syntax when Im next at my mac if needed.

then once ive fetched the right record - I declare 3 new customer variaes and use the results[0].addToCustomer() function like you have in one of your code snippets above. (By the looks of it you have a 1:many relationship between record and customers).

To create a new Record and add 4 customers (assuming the customers exist). you do the inverse. you do a fetch request on your customers with a predicate on customer to narrow down to the 4 customers you want. you then create a new Record by creating a new Record instance I would usually use something like:

let newRecord = Record(context:managedContext) then use the newRecord.addToCustomer() function again but loop through the array of results returned from your customer fetch request.

then to your third question - if you want edit the customer who is 58 and has an assocaited record woth a year of April 2021 then again its a fetch request on customer with a compound predicate. The first being age = 58. the second being record.year = 2012 and the third being record.month = April (this is assuming youve named your relationship record.

I havent watched that tutorial but you probably know that core data is more like a relational database than a heirarchical nesting of data (like xml or JSON). your Record entries are one thing. Your customer entries are another. they are joined by the relationship. You can bring the data out in the way you choose by using predicates and sorting.

and then if for example I wanted to display a list of records on screen and underneath each one I want to display a sub list of all the customers then I can do that as swift lets you use .notation and treats the many relationship like an array. so if I've fetched all my records then I can do a loop through them and say:

forEach (Records.customer) {customer in _ Text(customer.name) }

*thats swiftUi rather than swift but hopefully it conveys my point.

I hope Im understanding your question now. If not then I do apologise

3      

That sounds like a great place to start, thank you, when you've got the time is fine.

I am looking for a structured model, which I sould be able to achieve with Core Data, extracting as I need, so relationships are vital.

3      

Hi,

I have coded up the three scenarios we discussed above.

It's in swiftui (sorry but I don't get on much with uiKit), but I've taken all the logic out of the view.

The buttons in the view call functions which are stored in the Scenarios struct.

these scenario functions then call the core data functions in the DataManager class.

https://github.com/rlong405/coreDataRelationships

Richard

3      

Goodness, that's a lot of work on your part. I am very greatful that you made this available. Sorry for the late reply, I was busy this weekend, but have been working through the code to understand it and scale my needs.

The customerArray was really useful as Swift would complain of Sequence conformance when I attempted to itterate through it. Even though it was a part of the tutorial that I mentioned I originally had issues trying to get it to work.

Using NSPersistentStoreCoordinator before I think overcomplicated matters, sticking just with NSManagedObjects and fetchRequests achieves what I need with those tight relationships.

Many thanks for your help. Later on I shall need to experiement a lot more with relationships and comparissons, which is where Predicates could become very complicated, but very powerful, so I'll probably be in touch.

How are you finding SwiftUI, I'm not using it right now but it looks excellent.. what language do you hail from ?

3      

I hope you don't mind but I forked the code and refactored a few things to better suit my project needs, most notable was to merge some methods and pass through Swtich statements to determine specific data retrieval, as well as perform a check to see if data exists before writing.

https://github.com/rHockWorks/coreDataRelationships/tree/main/coreDataRelationships

Merging the Scenarios into a Struct was a good idea, but because my data is dynamically created I depreciated it.

Many thanks for your help, I hope this helps others !

3      

Hi

You are very welcome. It was nice to take a break from my app and just practice on something else for a few hours. Although, yes it did take a little longer than I had expected it to when I started. :-)

But I learnt some things as well as I was going along - for example, the customer array. I had thought that I'd done something like that before but I musn't have. so when it started complaining about the Set not being Sequence compliant, that was something I had to figure out..... so I guess I am saying that it benefitted me too.

In terms of SwiftUI, I wouldn't go back to UIKit now. I had realy problems getting my UI to layout how it needs to. With all the constraints against the view and constraints with each other etc. It's a pain in the neck. SwiftUI takes all that away. Then, with things like tableViews - you can create them simply by saying

List(arrayOFCustomers) { customer in 
Text(customer.firstName)
}

No more dequeing cells and delegates and the like.

So it's safe to say, I'm a big fan and even though I had to re-write probably 3/4 of my app into swiftui. It's something that was well worth the investment. If you find yourself with a spare hour or two then watch some youTube tutorials on it and see if you think you'd like it. The declarative nature of the language takes some getting use to and to be honest, I didn't quite understand what they meant when they talked about declarative but you soon just shift your brain into thinking that way.

In terms of which language I hail from - I don't really. I did a Business and IT degree at university where we did a bit of VB, PHP, some HTML, database design etc.(that was 15 years or so ago now) I decided that I wanted to build an app about 2 years ago and basically self taught myself Swift (and this last year SwiftUI). Knowing how relational databases work helps a lot. I'm still very much learning.

What about yourself? What project are you building if you don't mind me asking?

I'll take a look at how you've adopted the code, that'll be interesting.... I don't mind you forking it at all. It was created for you. Yes, the scenarios being in their own struct wouldn't be the normal procedure. I just wanted to keep everything together for you and away from my swiftUI code as I was cognizant of the fact that you've not used swiftUI so didn't want you to have to start unpicking the bits you want to copy / paste vs the view code you don't need.

and yes, definitely needed some checking on write to ensure data didn't already exist. I got lazy and instead put the reset button in :-)

By all means, feel free to get in touch if I can be of help. I enjoy keeping my brain sharp.

Incidentally, I can't help but ask but why are you using Strings for your dates? In your code comments, you indiciated there was a reason and it sounds like you hit an issue that required strings. Is there a particular challenge you've faced and can I try and help at all?

Richard

3      

I like how you rationalised some of the methods down by using enums on the predicates. I think I'll give that a try as it will help a lot

3      

Glad you didn't think I butchered your code. I always liked Enums as soon I as learned about them (I am also self taught), the idea of Swift being a "safe" language they seemed to make instant sense. I am only familliar with SwiftUI from a few videos, but because I'm using SpriteKit I cannot (yet) implement it.

I am making a game, in two dimentions (hence using SpriteKit) and it's been chugging a long for a couple of years now, it's a massive project for a first project, but I am learning a lot. I previously relied on Plists for persistent data, but it wouldn't scale, so I'm looking into Core Data to see how well it handles the data - that is why my data is dynamically generated. However, I shall be using Core Data in the future for other more standard applications.

I won't have any issues with Types, this was only dummy data. As you know I like Enums and can flip between the two without issue for use in Core Date, I found out that I need not mess around with Transmutable and just rely on default Strings, then simply place Static functions inside the Enum to convert the text to and from the Enum's case statements:

//return String rawValue
    static func withStringLabel(_ label: String) -> Category.RawValue? {
        return self.allCases.first{ "\($0)" == label }.map { $0.rawValue }
    }
//return Enum value
    static func withLabel(_ label: String) -> Category? {
        return self.allCases.first{ "\($0.rawValue)" == label }
    }

On the issue of Predicates and Enums, I wasn't happy with it as there could be a conflict of data, even with an Enum, and as the data is lined with the rawValue you could just pass through the one enum and both pieces of data are available.

SwiftUI is great because it doesn't allow for any hidden GUI changes (something I just lost two hours over as I forgot to change the class management to manual - I don't know why that isn't automatically done when exporting them for manual maintenance anyway?!) as the language matures.

I thin SwiftUI is really logical and easy to use. As you say about screen sizes, it's a constant annoyance when Apple releases new ones and HStacks/VStacks will solve that. But you should see the system that I need to employ for my game, especually as it's pixelart and not vector, it's a lot of work as pixelart must be pixel perfect !

I enjoyed the collaboration and glad that you benefitted, especially after running through so many turtorials on the issue which didn't really seem to allow me to achieve what I needed (CRUD), but there's always more than one way to achieve something.

Keep in touch !

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.