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.
-
\$\begingroup\$ Can you please give suggestions to improve this question, as this has been downvoted \$\endgroup\$saurabh– saurabh2017年01月10日 19:27:35 +00:00Commented 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\$πάντα ῥεῖ– πάντα ῥεῖ2017年01月10日 19:36:27 +00:00Commented 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\$saurabh– saurabh2017年01月10日 19:39:04 +00:00Commented Jan 10, 2017 at 19:39
1 Answer 1
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()
}
}
}