3
\$\begingroup\$

I have been using SwiftyJSON to create my models of Object in Swift. But recently, I moved to CoreData for saving the objects locally. I am new to CoreData and want to know the best practices I should follow for creating the model.

Here's what I have created to update and get User's data (after getting the JSON from webservice)

Core Data Model

import Foundation
import CoreData
import SwiftyJSON
@objc(User)
public class User: NSManagedObject {
var authToken : String!
class func addUser(fromJSON json: JSON?, inManagedObjectContext managedObjectContext: NSManagedObjectContext?) -> User? {
 if var json = json { //unwrapping
 let entityDescription = NSEntityDescription.entity(forEntityName: "User", in: managedObjectContext!)
 let newUser = NSManagedObject(entity: entityDescription!, insertInto: managedObjectContext) as! User
 json = json["user"]
 newUser.authToken = json["auth_token"].stringValue
 newUser.email = json["email"].stringValue
 managedObjectContext?.perform({
 _ = try? newUser.managedObjectContext!.save()
 })
 return newUser
 }
 return nil
}
class func getUser(inManagedObjectContext managedObjectContext: NSManagedObjectContext) -> User? {
 // Initialize Fetch Request
 let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
 // Create Entity Description
 let entityDescription = NSEntityDescription.entity(forEntityName: "User", in: managedObjectContext)
 // Configure Fetch Request
 fetchRequest.entity = entityDescription
 if let result = try? managedObjectContext.fetch(fetchRequest) {
 return result.first as? User
 } else {
 return nil
 }
}
class func updateUser(fromJson json : JSON?, inManagedObjectContext managedObjectContext: NSManagedObjectContext) {
 let user = getUser(inManagedObjectContext: managedObjectContext)
 if var json = json {
 json = json["user"]
 user?.name = json["name"].stringValue
 user?.email = json["email"].stringValue
 }
 managedObjectContext.perform({
 _ = try? managedObjectContext.save()
 })
}
}

Here's how I used to create model before I was using Core Data:

Previous Model

import Foundation
import SwiftyJSON
class User : NSObject, NSCoding{
var email : String!
var name : String!
/**
 * Instantiate the instance using the passed json values to set the properties values
 */
init(fromJson json: JSON!){
 if json == nil{
 return
 }
 email = json["email"].stringValue
 name = json["name"].stringValue
}
}

Although the code is working, I am not sure if this is the right way to do it. How should I actually create the model with CoreData? And how should I call them from ViewControllers? I tried reading tutorials, but they help on getting started but not on how to use them in bigger projects.

asked Jan 10, 2017 at 19:23
\$\endgroup\$
3
  • \$\begingroup\$ Can you please give suggestions to improve this question, as this has been downvoted \$\endgroup\$ Commented Jan 10, 2017 at 19:27
  • \$\begingroup\$ Seems you're asking about (completely) rewriting your current code, adding some none yet existing code respectively, instead of improvement of what you have already. Such is considered off-topic at Code Review. \$\endgroup\$ Commented Jan 10, 2017 at 19:36
  • \$\begingroup\$ No I am not asking for rewriting the current code. Just want to know if this well-practiced way, or needs improvements. \$\endgroup\$ Commented Jan 10, 2017 at 19:39

1 Answer 1

1
+50
\$\begingroup\$

Based on the code that you've supplied, here's my take on some small changes in responsibility.

First of all, the NSManagedObject subclass.

You can write this by hand (as I think you have done) but I prefer to have Xcode generate this subclass automatically from the xcdatamodeld file (this option is found in the 'Editor' menu).

Note that in the latest version of Xcode, this file isn't added to your project, but actually added to the 'DerivedData' folder so unless you go looking for it you'll probably never see it.

If you find it and open it up, it will probably look something like this this:

import Foundation
import CoreData
@objc(User)
public class User: NSManagedObject {
 @NSManaged public var name: String?
 @NSManaged public var email: String?
 @NSManaged public var authToken: String?
}

Second, an extension that contains some methods that are specific to working with JSON objects.

Instead of a class function to create a User object, I'd use a failiable initializer init?() that returns nil if an instance could not be created.

I'd also create some way to reference your string keys so that you're not typing them by hand each time. This could be a struct, or just a bunch of static let statements. I prefer the struct because it puts all the keys in a similar namespace that helps organize your code, but also helps with auto completion when you're typing out the keys.

import SwiftyJSON
extension User {
 // prevent accidental typos when working with string keys
 struct Keys {
 static let name = "name"
 static let email = "email"
 static let authToken = "auth_token"
 static let user = "user"
 }
 // convenience initalizer for making `User` from a JSON object
 convenience init?(in context: NSManagedObjectContext, with json: JSON?) {
 guard let json = json else { return nil }
 email = json[Keys.email].stringValue
 authToken = json[Keys.authToken].stringValue
 }
 // updating existing instance with data from a JSON object
 func update(from json: JSON?) {
 guard let json = json[Keys.user] else { return }
 name = json[Keys.name].stringValue
 email = json[Keys.email].stringValue
 }
}

Finally, I'd remove any code that calls save() on your context from within the User object, and have this in the controller. I'd probably also remove the code that fetches a User and also have this moved into the view controller.

Personally, I'd also not call save() on the object when it's newly created, and instead defer that until some later time (such as when the view controller is exiting).

import UIKit
class ViewController: UIViewController {
 var context: NSManagedObjectContext?
 func someMethodThatCreatesUser() {
 guard let context = context else { return }
 let json: JSON? // ... grab json
 guard let user = User(in: context, with: json) else { return }
 // do something with the user...
 }
 func someMethodThatGetsExistingUser() -> User? {
 guard let context = context else { return nil }
 let request: NSFetchRequest<User> = User.fetchRequest() 
 // `fetchRequest()` is auto generated by Xcode, if you create
 // your own managed object subclasses you'll need to do some
 // additional work here.
 let users = try? context.fetch(request)
 return users?.first
 }
 deinit {
 guard let context = context else { return }
 context.perform {
 try? context.save()
 }
 }
}
answered Jan 17, 2017 at 19:13
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.