3
\$\begingroup\$

I built a wrapper in Swift to practice CoreData.

My question is more general, I hope this is acceptable. I wanted to know whether my implementation of this wrapper is correct or poorly implemented for practical use. Any feedback like design-patterns, conventions, etc are welcome as well.

Currently I have a class I reference to manage Entities:

class SwiftCoreDataHelper {

    //Get saved data
    func getData(forEntity: String, andSaveToArray entityArray: inout [NSManagedObject]) {

        //Get managedContext, refrence to AppDelegate, and prepare fetchRequest
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
        let managedContext = appDelegate.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "\(forEntity)")

        //Get saved data
        do {
            entityArray = try managedContext.fetch(fetchRequest)
        } catch let error as NSError {
            print("Could not fetch. \(error), \(error.userInfo)")
        }
    }

    //Save data
    func save(name: String, useEntity nameOfEntity: String, useArray entityArray: inout [NSManagedObject], usingKeypathName appropriateKeyPathName: String) {

        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
        let managedContext = appDelegate.persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: nameOfEntity, in: managedContext)!
        let genericItem = NSManagedObject(entity: entity, insertInto: managedContext)
        genericItem.setValue(name, forKeyPath: appropriateKeyPathName)

        do { //Save context and add to array
            try managedContext.save()
            entityArray.append(genericItem)
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }
    }

    //Updating stored data
    func updateData(forEntity: String, updateValueTo updatedValue: String, andSaveToArray entityArray: inout [NSManagedObject]){

        //Get managedContext, refrence to AppDelegate, and prepare fetchRequest
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
        let managedContext = appDelegate.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "\(forEntity)")

        do {
            let fetched = try managedContext.fetch(fetchRequest)
            let objectUpdate = fetched.last! as! NSManagedObject

            //Update value
            objectUpdate.setValue(updatedValue, forKey: "name")

            do { //Save context
                try managedContext.save()
            }
            catch {
                print(error)
            }
        }
        catch {
            print(error)
        }
    }

    //Delete saved data
    func deleteData(forEntity: String){

        //Get managedContext, refrence to AppDelegate, and prepare fetchRequest
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
        let managedContext = appDelegate.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "\(forEntity)")

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

            //Delete object
            let objectToDelete = fetched as! [NSManagedObject]
            if (objectToDelete.count != 0){ //Do not delete if nothing to delete
                managedContext.delete(objectToDelete.last!)
            }

            do { //Save context
                try managedContext.save()
            }
            catch {
                print(error)
            }
        }
        catch {
            print(error)
        }
      }
    }

Then I use the wrapper as follows:

let myData = SwiftCoreDataHelper()
var people: [NSManagedObject] = []

//Write data to entity previously created in xcdatamodeld file
let personName = "Harry"
myData.save(name: personName, useEntity: "Person", useArray: &people, usingKeypathName: "name")

//Fetch data saved in an Entity:
myData.getData(forEntity: "Person", andSaveToArray: &people)
Update data in an entity as follows

//Update data in an Entity
let otherName = "Tom"
myData.updateData(forEntity: "Person", updateValueTo: otherName, andSaveToArray: &people)
Remove an entity entirely as follows

//Delete all data in an Entity
myData.deleteData(forEntity: "Person")
\$\endgroup\$
2
\$\begingroup\$

Well, if it works for you, it's fine :)

Here are the things that bother me:

  • Because inserting new objects in Core Data is quite an important operation, and is independent of saving, and since two of your other functions save the context as a side effect, you should change the symbol name save() to something involving insert.

  • Saving the context as a side effect any time you change any single managed object in any way will probably incur a performance penalty if many objects are changed, for example if pulling from a server, relative to instead saving after all changes are done.

  • Your deleteData(forEntity:) function appears to delete only the "last" object returned by a fetch, which is indeterminate unless the fetch request has a sort descriptor, but in your function it does not. In other words, this function will delete one object of the given entity, but the caller does not know which one. This is probably not the behavior you were expecting.

That should be enough to get you started. There may be other issues.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.