Swift - 核心数据种子类 [英] Swift - Core Data Seeding Class

查看:173
本文介绍了Swift - 核心数据种子类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



.com / 2015/02/25 / using-swift-to-seed-a-core-data-database /rel =nofollow> http://www.andrewcbancroft.com/2015/02/25/using- swift-to-seed-a-core-data-database /



我的数据库使用JSON填充,虽然我想复制模式作者



在文章中,他提到这种方法违反了一次性使用的责任。我知道类应该承担一个责任,但是考虑到一个情况,例如我的,我需要种子相当大的数据集,当用户登录,例如,有另一种方法吗?



我道歉,如果这是因为煽动讨论,这不是我的意图,我的问题是这种风格的种子是生产中常见的,如果没有,什么是最好的模式实现种类的数据种子。

解决方案

我不认为有可能真正回答每个人在生产中导入数据,不同的东西。



相反,我只想提到,根据苹果的核心数据编程指南,导入数据的最有效的方法是通过批量导入过程。此过程详见此处。



https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html



这样说,我会将您的数据存储在一个JSON文件中,该文件存储在Web服务或应用程序包中作为资源,然后使用NSJsonSerialization类将其转换为基础对象,您的代码可以推理。然后,我将使用上面指南中概述的原则来创建批量导入过程,为您的数据库创建种子。



这是很多,苹果的例子很简单。我还会说,最好在后台线程上运行此进程,因为如果导入需要很长时间才能完成,操作系统可能会终止您的应用程序。



希望这个帮助!



* EDIT *



使用协议和泛型来对任何类型的对象执行任务。您可以使用此模式执行任何类型的操作,因此只需接受概念并输入您的核心数据逻辑。



这只是一个模式的示例,并且不应被视为即插即用实现。它将需要适应支持核心数据批量导入和保存。然而,它清楚地显示了一种获取字典或字典数组的方法,并将它们解码为对象。

 协议JSONDecodable {

//这被使用,所以你可以有一个统一的方式实例化而不依赖子类NSObject
init()

//这可以实现,所以你可以手动解码单个对象的类型从字典
static func decodeFromJSON(json:AnyObject?) - > AnyObject?

//这是为了可以手动设置对象的值。这是必需的,因为Swift反射不支持动态属性设置
func setValueForKey(value:AnyObject ?, forKey:String)
}

//此类负责解码一个JSON字典到一个对象
class JSONDecoder< T:JSONDecodable> ;: NSObject {

// MARK:初始化

需要重写init(){
//做任何初始化
}

// MARK:公共方法

/ **
从JSON创建一个对象。只有当JSON只包含单个对象时才使用此方法

:json:数据字典
:返回:给定类型的对象
* /
func toSingle(json:AnyObject?) - > T? {

//处理单个对象,并返回一个数组与一个对象
如果let dict = json as? [NSObject:AnyObject] {
return self.makeObject(dict)
}

return nil
}

/ **
从JSON创建对象列表。这个方法应该只在JSON包含多个对象时使用

:json:数据字典
:返回:给定类型的对象列表
* /
func toArray(json:AnyObject?) - > [T]? {

//进程数组
如果let arr = json as? [AnyObject] {
return self.makeObjects(arr)

}否则如果let dict = json as? [NSObject:AnyObject] {
//处理单个对象,并返回一个数组
var arr = [T]()
arr.append(self.makeObject )
return arr
}

return nil
}

// MARK:魔法

private func makeObjects(jsonArray:[AnyObject]?) - > [T]? {

var returnArray:[T] = [T]()

如果let jArray = jsonArray {
for jObject in jArray {
if let dict = jObject as? [NSObject:AnyObject] {
returnArray.append(self.makeObject(dict))
}
}
}

if returnArray.count> 0 {
return returnArray
} else {
return nil
}
}

private func makeObject(jsonDict:[NSObject:AnyObject] ) - > T {

var returnObject = T.self()//这是协议中的init()函数派上用场的地方。它允许我们使用泛型来创建我们的对象的动态实例

for(key,value)in jsonDict {
if let k = key as? String {
returnObject.setValueForKey(value,forKey:k)//这是协议中的setValueForKey(value:AnyObject ?,,forKey:String)函数派上用场的地方。它允许我们让对象自己设置它自己的值。
}
}

return returnObject
}
}

//这是一个实现协议的示例类,意味着它可以通过解码过程发送
类Employee:NSManagedObject,JSONDecodable {

// MARK: - 属性
var employeID:Int!
var name:Int!
var hireDate:NSDate?
var部门:部门?

// MARK: - 初始化
覆盖所需的init(){
//必须满足JSONDecodable协议
}

static func decodeFromJSON(json:AnyObject?) - > AnyObject? {

var decoder = JSONDecoder< Employee>()
return decoder.toSingle(json)
}

func setValueForKey(value:AnyObject? forKey:String){

switch(forKey){
caseemployeID:
self.employeID = value as! Int

casename:
self.name = value as! String

casehireDate:

如果let v = value as? String {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat =MM / dd / yyyy
self.hireDate = dateFormatter.dateFromString(v)
}

casedepartment:
如果let v = value as? [NSObject:AnyObject] {

如果let dept = Department.decodeFromJSON(dict)as?部门{
self.department = dept
}
}

默认值:
NSLog([setValueForKey]无法找到属性\(forKey) )
}
}
}


I am following the current tutorial on creating a large class to handle seeding a large database with data.

http://www.andrewcbancroft.com/2015/02/25/using-swift-to-seed-a-core-data-database/

My database is populated using JSON, though I am wanting to copy the pattern the author uses in the above article.

During the article he mentions this approach violates the single-use responsibility. I am aware that classes should take a single responsibility, but given a situation such as mine where I will need to seed quite a large dataset when the user logs in for example, is there another approach to take?

I apologise if this comes off as inciting a discussion, that isn't my intention, my question is wether this style of seeding is commonplace in production or if not, what is the best pattern to implement this kind of data seeding.

解决方案

I don't think it's possible to really answer how everyone imports data in production as everyone could do different things.

Instead, I just want to mention that according to Apple's "Core Data Programming Guide" the most efficient way to import data is via a batch import process. This process is detailed here.

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html

With that said I would store your data in a JSON file that is stored either on a web service, or in the app bundle as a resource, and then use the NSJsonSerialization class to convert it to foundation objects that your code can reason with. Then I would use the principals outlined in the guide above to create a bulk import process to seed your database.

That's pretty much it, and Apple's examples are pretty straight forward. I would also state it would be best to run this process on a background thread as the OS may terminate your application if the import takes a long time to complete.

Hope this helps!

* EDIT *

Here is an example of how you can use protocols and generics to perform tasks against any type of object. You can use this pattern to perform any type of operation, so just take the concept and enter your Core Data logic.

This is merely an example of a pattern one could follow, and shouldn't be considered a plug-in-play implementation. It will need to be adapted to support the Core Data bulk import and saving. However, it does clearly show a way to take a dictionary, or array of dictionaries, and decode them to objects. Then what you do with your objects is completely up to you.

protocol JSONDecodable {

    // This is used so you can have a unified way to instantiate an instance without relying on sub-classing NSObject
    init()

    // This can be implemented so you can manually decode a single object of the type from a dictionary
    static func decodeFromJSON(json: AnyObject?) -> AnyObject?

    // This is used so that you can manually set the values to the object. This is required as Swift reflection doesn't support dynamic property setting
    func setValueForKey(value: AnyObject?, forKey: String)
}

// This class is responsible for decoding a JSON dictionary into an object
class JSONDecoder<T:JSONDecodable>: NSObject {

    //MARK: Initialization

    required override init() {
        // do any initialization here
    }

    //MARK: Public Methods

    /**
        Creates a single object from the JSON. This method should only be used if the JSON will only ever contain a single object

        :json: A dictionary of data
        :returns: An object of the given type
    */
    func toSingle(json: AnyObject?) -> T? {

        // process single object, and return an array with the one object
        if let dict = json as? [NSObject: AnyObject] {
            return self.makeObject(dict)
        }

        return nil
    }

    /**
        Creates a list of objects from the JSON. This method should only be used if the JSON will contain multiple objects

        :json: A dictionary of data
        :returns: An list of objects of the given type
    */
    func toArray(json: AnyObject?) -> [T]? {

        // process array
        if let arr = json as? [AnyObject] {
            return self.makeObjects(arr)

        } else if let dict = json as? [NSObject: AnyObject] {
            // process single object, and return an array with the one object
            var arr = [T]()
            arr.append(self.makeObject(dict))
            return arr
        }

        return nil
    }

    //MARK: The Magic

    private func makeObjects(jsonArray: [AnyObject]?) -> [T]? {

        var returnArray: [T] = [T]()

        if let jArray = jsonArray {
            for jObject in jArray {
                if let dict = jObject as? [NSObject: AnyObject] {
                    returnArray.append(self.makeObject(dict))
                }
            }
        }

        if returnArray.count > 0 {
            return returnArray
        } else {
            return nil
        }
    }

    private func makeObject(jsonDict: [NSObject: AnyObject]) -> T {

        var returnObject = T.self() // this is where the init() function in the protocol comes in handy. It allows us to use generics to create a dynamic instance of our object

        for (key, value) in jsonDict {
            if let k = key as? String {
                returnObject.setValueForKey(value, forKey: k) // this is where the setValueForKey(value: AnyObject?, forKey: String) function in the protocol comes in handy. It allows us to let the object it's self set it's own values.
            }
        }

        return returnObject
    }
}

// This is an example class that implements the protocol which means it can be sent through the decoding process
class Employee: NSManagedObject, JSONDecodable {

    //MARK: - Properties
    var employeID: Int!
    var name: Int!
    var hireDate: NSDate?
    var department: Department?

    //MARK: - Initialization
    override required init() {
        // Necessary to satisfy the JSONDecodable protocol
    }

    static func decodeFromJSON(json: AnyObject?) -> AnyObject? {

        var decoder = JSONDecoder<Employee>()
        return decoder.toSingle(json)
    }

    func setValueForKey(value: AnyObject?, forKey: String) {

        switch (forKey) {
        case "employeID":
            self.employeID = value as! Int

        case "name":
            self.name = value as! String

        case "hireDate":

            if let v = value as? String {
                let dateFormatter = NSDateFormatter()
                dateFormatter.dateFormat = "MM/dd/yyyy"
                self.hireDate = dateFormatter.dateFromString(v)
            }

        case "department":
            if let v = value as? [NSObject: AnyObject] {

                if let dept = Department.decodeFromJSON(dict) as? Department {
                    self.department = dept
                }
            }

        default:
            NSLog("[setValueForKey] Unable to find property \(forKey)")
        }
    }
}

这篇关于Swift - 核心数据种子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆