How to use NSSet created from Core Data
Asked Answered
W

2

5

I have the following core data model: enter image description here enter image description here

where Person to Codes is a one-to-many relationship.

I have a function which returns a Person record and if the code person.codes returns an NSSet of all the codes associated with that Person. The issue that I am having is how to use the NSSet.

person.codes.allObjects.first returns this data:

<Codes: 0x60000213cb40> (entity: Codes; id: 0xb978dbf34ddb849 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Codes/p1> ; data: {
    code = 4LQ;
    number = 1;
    whosAccount = "0xb978dbf34ddb869 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Person/p1>";
})

I thought if I made person.codes.allObjects.first of type Codes, I would be able to access the code and number elements but I get an error: error: value of type 'Any?' has no member 'number'

Also, how can I search this data set for a particular code or number.

I appreciate that this is proabably a simple question but have searched and read the documentation to no avail. I suspect that may base knowledge is not sufficient.

Update

I have a CoreDataHandler class which contains the following code:

class CoreDataHandler: NSObject {
    //static let sharedInstance = CoreDataHandler()

    private static func getContext() -> NSManagedObjectContext {
        let appDelegate = NSApplication.shared.delegate as! AppDelegate

        return appDelegate.persistentContainer.viewContext
    }

    static func fetchPerson() -> [Person]? {
        let context = getContext()

        do {
            let persons: [Person] = try context.fetch(Person.fetchRequest())
            return persons
        } catch {
            return nil
        }
    }

I can fetch a person using:

let row = personTableView.selectedRow
let person = CoreDataHandler.fetchPerson()?[row]
Weswesa answered 22/11, 2018 at 20:52 Comment(2)
All objects takes the NSSet and makes an arrayWeswesa
map it into an array first, if you need an index-based collection.Silden
F
15

Core Data supports widely native Swift types.

Declare codes as Set<Codes> in the Person class.

It's much more convenient than typeless NSSet.
You get a strong type and you can apply all native functions like filter, sort, etc. without type cast.

Fatshan answered 22/11, 2018 at 22:50 Comment(11)
I realize that it is probably an obvious question but how do I actually declare codes in the Person class?Weswesa
You have to create the NSManagedObject subclasses manually. In the model file in the Entity Inspector change the Codegen popup of all entities to Manual/None, then in Menu Editor > Create NSManagedObject Subclass create the classes and change the types.Fatshan
I have done that and have a Person class which looks like this:public class Person: NSManagedObject { }. How do I declare codes in that class?Weswesa
There are two files per entity. A +CoreDataClass file and a +CoreDataProperties file. The properties are declared in the +CoreDataProperties file.Fatshan
In my Person+.. file, I have: @NSManaged public var codes: Set<Codes>?. Now in my viewController class, this code works: let codes = person?.codes; let filteredCodes = codes!.filter({ $0.number == 1 }); print("key for code1= \(filteredCodes)") and prints out: key for code1= [<Codes: 0x60000213d360> (entity: Codes; id: 0xe7b68aa93f25f785 <x-coredata://.........> ; data: { code = 4LQ; number = 1; whosAccount = "0xe7b68aa93f25f7a5 <x-coredata://.......>"; })]. But filteredCodes.code is not recognised. How do I get to just the code, 4LQ?Weswesa
First of all declare a to-many relationship as non-optional (Set<Codes>). Uncheck Optional also in the model file for the relationship. The result of filter is an array (note the []), If you want to get one item use first (which returns an optional).Fatshan
Changed to non-optional and unchecked Optional also but let filteredCodes = codes!.filter({ $0.number == 1 }) and let code = filteredCodes.first both print out the same info: key for code1= [<Codes: 0x60000213d360> (entity: Codes; id: 0xe7b68aa93f25f785 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Codes/p1> ; data: { code = 4LQ; number = 1; whosAccount = "0xe7b68aa93f25f7a5 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Person/p1>"; })]Weswesa
No, let filteredCodes = codes!.first{ $0.number == 1 }Fatshan
Got it. Thank you very much!Weswesa
I wonder why the autodefined classes after creating an entity doesn't do so. It's the behaviour everyone would expectMader
@Mader I found this: mjtsai.com/blog/2021/03/31/…Elfland
A
4
let codes = person.codes as! Set<Code>

Once that is done you can access the properties. Searching can be done by filtering for instance

let filteredCodes = codes.filter({ $0.code == "XYZ" })

will return all objects that has the code "XYZ". Or to get only one you can use

let code = codes.first(where: {$0.id == 1})

which will return the first object that has id = 1

A simple example getting all Person objects that has a given code

func findWithCode(_ code: String) -> [Person] {
    guard let persons = CoreDataHandler.fetchPerson() else {
        return []
    }

    var result = [Person]()
    for person in persons {
        let codes = person.codes as! Set<Code>
        if codes.contains(where: { $0.code == code }) {
            result.append(person)
        }
    }

    return persons
}
Albi answered 22/11, 2018 at 21:36 Comment(12)
I get this error when I try and typecast the set as Codes: Cast from 'NSSet?' to unrelated type 'Codes' always fails`Weswesa
Have you tried as a native type let codes: Set<Codes> = person.codes?Albi
If I try let codes: Set<Codes> = person.codes, I get this error: Cannot convert value of type 'NSSet?' to specified type 'Set<Codes>Weswesa
@Weswesa My answer is incorrect in the typecasting, I'll updateAlbi
if I try let codes = person.codes as! [Codes], I get this error: Cast from 'NSSet?' to unrelated type '[Codes]' always failsWeswesa
Ah, you are missing a question mark since your method returns [Persons]?, try to change it to let codes = person?.codes as! [Codes]. Or if let codes = person?.codes as! [Codes] {...}Albi
I still get the error: Cast from 'NSSet?' to unrelated type '[Codes]' always failsWeswesa
No, as I am not sure how to do that. My Person class looks like this:public class Person: NSManagedObject { }Weswesa
@Weswesa I did setup my own version of your model and used that to improve my answerAlbi
this line let codes = person.codes as! Set<Codes> would not work for me. The only way I could get it to work was to make this declaration @NSManaged public var codes: Set<Codes>? in my Person class extension. I don't understand why cast the NSSet as Codes in my ViewControllerWeswesa
Ok. Well I did create an Xcode project with the same entities as yours and the above code works for me so there is nothing more I can do now. I also tried changing the declaration of codes in the Person class to Set<Codes> and that also worked. Anyway I guess you have a working solution by now using the other answer so all is goodAlbi
I do have a working solution but it frustrates that I don’t understand why it wasn’t workWeswesa

© 2022 - 2024 — McMap. All rights reserved.