Swift CoreData 使用及Transformable多类型存储

2,760 阅读2分钟

最近开始学习Swift,想使用纯Swift完成一个本地事件薄APP,其中数据存储采用CoreData,下面记录CoreData在使用中的几个问题点,欢迎指教!!!

首先, 创建CoreData,步骤就不细说了,这里主要有两个问题 第一个就是选择值类型的时候,如果要存储类似于字典、数组或者其他类型的时候,选择Transformable类型,这个类型从字面意思来理解为可转换类型。选择后,选中这条数据,如下图

其他类型转换 可以看到在Trasnformer这栏填上自己定义的类,这个类需要继承于ValueTransformer

下面贴上我的代码

@objc(SSLDictionaryModel)
final class SSLDictionaryModel: ValueTransformer {

    override func transformedValue(_ value: Any?) -> Any? {

        guard let dict = value as? NSDictionary else{
            return nil
        }
        do {
            if #available(iOS 11.0, *) {
                let data = try NSKeyedArchiver.archivedData(withRootObject:dict, requiringSecureCoding: true)
                return data
            } else {
                // Fallback on earlier versions
                return NSKeyedArchiver.archivedData(withRootObject:dict)
            }
        }catch{
            assertionFailure("Failed to transform 'NSDictionary' to 'Data'")
            return nil
        }
    }
    
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let data = value as? NSData else{return nil}
        do {
            if #available(iOS 11.0, *) {
                let dict = try NSKeyedUnarchiver.unarchivedObject(ofClass:NSDictionary.self,  from:data as Data)
                return dict
            }else {
                guard let dict = NSKeyedUnarchiver.unarchiveObject(with: data as Data)else{
                    return nil
                }
                return dict
            }
        }catch{
            assertionFailure("Failed to transform 'Data' to 'NSDictionary'")
            return nil
        }
    }
    
    override class func allowsReverseTransformation() -> Bool {
        return true
    }

    override class func transformedValueClass() -> AnyClass {
        return NSDictionary.self
    }
}

如果要转换其他类型,比如数组,则将NSDictionary改成NSArray,其他对应改就OK。

第二点就是在创建模型的时候需要选择是系统隐性生成还是手动的生成,系统默认是隐式生成模型类(Class definition),这时如果自己再手动的创建会出现文件冲突

文件手动生成 选择后再在Xcode的Editor中选择Create NSManagedObject Subclass 手动的创建模型

至此,数据库模型创建完成

下面是数据库的使用 创建数据库

func setupData(name: String){

        let url = Bundle.main.url(forResource: "SSLCoreData", withExtension: "momd")!

        guard let managedObjectModel = NSManagedObjectModel(contentsOf: url)else{

            fatalError("Manager object model could not be created.")

        }

        persisContext = NSPersistentContainer(name: name, managedObjectModel: managedObjectModel)
        persisContext?.loadPersistentStores(completionHandler: { storeDescription, error in

            if let err = error as NSError?{

                fatalError("Unresolved error\(err.userInfo)")

            }

        })

    }

数据查询

func requestFoodSettingData(_ page: Int = 0, result: @escaping(_ data: Array<SSLFoodListSettingModel>, _ status: Bool)->Void){
        if persisContext == nil{
            return
        }
        requestQueue.async { [weak self] in
            let fetchRequest = NSFetchRequest<SSLCoreSetting>(entityName: "SSLCoreSetting")
            fetchRequest.fetchLimit = 100;
            fetchRequest.fetchOffset = page;
            let entity = NSEntityDescription.entity(forEntityName: "SSLCoreSetting", in: (self?.persisContext!.viewContext)!)
            fetchRequest.entity = entity
            do{
                if let fetchedObject = try self?.persisContext?.viewContext.fetch(fetchRequest){
                    var data : Array<SSLFoodListSettingModel> = []
                    for  foodSetting in fetchedObject{
                        let foodSettingModel = SSLFoodListSettingModel()
                        foodSettingModel.createTime = foodSetting.createTime
                        foodSettingModel.tag = SSLFoodListType(rawValue: Int(foodSetting.tag))
                        if (foodSetting.dataKeys != nil){
                            foodSettingModel.dataKeys = foodSetting.dataKeys as? [String]
                        }
                        foodSettingModel.isUpdateKeys = foodSetting.isUpdateKeys
                        data.append(foodSettingModel)
                    }
                    result(data, true)
                }
                else
                {
                    result([],false)
                }
            }catch{
                result([],false)
            }
        }
    }

插入数据

func reqeustInsertFoodSettingData(_ model: SSLFoodListSettingModel, result: @escaping(_ status: Bool)-> Void){
        requestQueue.async { [weak self] in
            if ((self?.persisContext) != nil){
                let settingModel : SSLCoreSetting = NSEntityDescription.insertNewObject(forEntityName: "SSLCoreSetting", into: (self?.persisContext!.viewContext)!) as! SSLCoreSetting
                settingModel.createTime = model.createTime
                settingModel.listId = model.listId
                settingModel.tag = Int16(model.tag?.rawValue ?? 0)
                if (model.dataKeys != nil){
                    settingModel.dataKeys = model.dataKeys as NSObject?
                }
                settingModel.isUpdateKeys = model.isUpdateKeys
                do {
                    try self?.persisContext?.viewContext.save()
                    result(true)
                }catch{
                    result(false)
                }
            }
            else
            {
                result(false)
            }
        }
    }

修改数据和删除数据和查询逻辑基本雷同,就不做演示了,贴上操作API

删除
self?.persisContext?.viewContext.delete(settingModel!)
修改
self?.persisContext?.viewContext.save()

至此,简单的CoreData存储基本都能实现了

由于APP比较简单,目前没做太多的封装和处理,如有问题,欢迎提出指正,谢谢