微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

swift-CoreData数据库结合plist文件预置数据案例

1、前言

本文主要记录swift开发Core Data数据库相关知识,从项目搭建,到plist数据准备,再到数据库的增删改查。并且提供源码免费下载。好记性不如烂笔头,记录一下方便以后学习,毕竟是初学者,哈哈,学无止境,以后会持续不断更新。

2、环境搭建

2.1、新建CoreData工程

新建包含CoreData的工程,xCode->File->New->Project,下面一步时,记得勾选“Use Core Data”。

在这里插入图片描述


新建完成之后,会看到工程根目录下有个和工程名称一模一样的后缀名为"xcdatamodeld"的文件,该文件就是数据库文件。再打开工程的AppDelegate可以看到有关该文件全局变量和全局方法自动生成

    // MARK: - Core Data stack
    lazy var persistentContainer: NSPersistentContainer = {
        //CoredataPractice为数据库文件名称,可以修改,但必须一致。
        let container = NSPersistentContainer(name: "CoredataPractice")
        container.loadPersistentStores(completionHandler: { 
        (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()
    // MARK: - Core Data Saving support
    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

2.2、旧工程引入CoreData

新建时没有选择use core data,也可以随时新建数据库文件,如下:

在这里插入图片描述


新建的文件命名可以随意,新建完成之后,这里取名为test,打开AppDelegate,将上面的那段自动生成代码加进去,特别注意NSPersistentContainer对象初始化的name必须与后缀名为xcdatamodeld文件名一模一样。

3、数据库文件维护

打开数据库文件,界面如下:

在这里插入图片描述

3.1、Entity/Attribute

点击Add Entity,新建一个Entity,这里相当于一个数据库表。在里面增加Attribute,相当于数据库表字段。Attribute有很多种类型,如下:

在这里插入图片描述


除了一些常用的基本数据类型外,Transformable类型可以包含数组,字典等。选择完成数据类型之后可以对该字段做一些个性化的设置,如下图:

在这里插入图片描述


设置字段是否是可选,最小最大长度,认值,或者正则表达式等等。注意如果数据库中已有数据,再要修改数据库某字段的类型,再次运行到手机上之后,程序会崩溃,需先卸载再安装,当然仅限于开发阶段。

3.2、Relationship

关联关系这个应该是经常用到的了,比如本案例,数据库表字段如下:
Cartoon动画片表

字段 类型 说明
name String 动画片名称

Character人物角色表

字段 类型 说明
name String 角色名称
isProtagonist Bool 是否主角
age Int 16 年龄
remark String 备注

一部动画片对应多个人物角色。此时需要建立关联关系。在Cartoon表中建立Relationship,取名为roles,如下图:

在这里插入图片描述

  • Relationship命名为roles。
  • Destination选择Character。
  • 右边Delete Rule选择为Cascade,即关联删除,如果删除了这个动画片,就会同样删除动画片关联的所有人物角色。
  • 右边type选为To Many即一对多。

在Character表中同样需要建立关系,这个人物所属的动画,如下图:

在这里插入图片描述

  • Relationship命名为cartoon_name。
  • Destination选择Cartoon。
  • Inverse选择roles,即对应Cartoon表中的关系名称
  • 右边Delete Rule选择为Nullify,无任何关联。
  • 右边type选为To One即一对一,一个人物角色只能对应一部动画片。

此时可以回到Cartoon表添加roles关系的指向了,如下图:

在这里插入图片描述


至此,一个完整的关联关系建立起来了。至于如何赋值,可以参考后文5.3。

4、plist文件操作

plist文件可以给数据库预置一些数据。新建plist文件,New File->Property List(Resource)。如下:

在这里插入图片描述


新建完成之后,设置根节点类型,这里选为Array,Item类型选为字典

在这里插入图片描述


添加一些字段,及对应的数据

在这里插入图片描述


至此准备工作完毕。

5、数据库读写

5.1、读取plist文件数据

		//该段代码是读取datalist.plist文件中的所有数据
        let plistPath : String = Bundle.main.path(forResource: "datalist.plist", ofType:nil)!
        let plistArray : NSArray = NSArray(contentsOfFile: plistPath)!
        for item in plistArray {
            let itemDict: [String: Any] = item as! [String : Any]
            print(item)
        }

5.2、数据库增查方法

    /// Cartoon插入记录
    ///
    /// - Parameter name: 动画片名称
    /// - Returns: Cartoon对象
    func saveCartoon(name : String) -> Cartoon {
        //创建对象
        let obj = NSEntityDescription.insertNewObject(forEntityName: "Cartoon", into: context!) as! Cartoon
        //对象赋值
        obj.name = name
        
        //保存
        app?.saveContext()
        
        return obj
    }
    
    /// Character插入数据
    ///
    /// - Parameters:
    ///   - name: 角色名称
    ///   - age: 年龄
    ///   - isProtagonist: 是否主角
    ///   - cartoon_name: 动画片对象
    ///   - remark: 备注
    /// - Returns: Character对象
    func saveCharacter(name: String,
                       age: Int16,
                       isProtagonist: Bool,
                       cartoon_name: Cartoon,
                       remark: String? = nil) -> Character {
        let obj = NSEntityDescription.insertNewObject(forEntityName: "Character",
                                                      into: context!) as! Character
        obj.name = name
        obj.age = age
        obj.isProtagonist = isProtagonist
        obj.cartoon_name = cartoon_name
        obj.remark = remark ?? ""
        
        app?.saveContext()
        return obj
    }

    /// 查询Cartoon
    ///
    /// - Parameter name: 名称
    /// - Returns: 结果数组
    func queryCartoon(name : String? = nil) -> [Cartoon]{
        //声明数据的请求
        let fetchRequest = NSFetchRequest<Cartoon>(entityName: entity_Cartoon)
        if name != nil {
            //设置查询条件
            let predicate = nspredicate(format: "name==%@", name!)
            fetchRequest.predicate = predicate
        }
        fetchRequest.fetchOffset = 0 //查询的偏移量
        
        //查询操作
        do {
            let fetchedobjects = try context!.fetch(fetchRequest)
            print(String.init(format:"%@.count = %i", entity_Cartoon, fetchedobjects.count))
            return fetchedobjects
        }
        catch {
            fatalError("查询异常“:\(error)")
        }
        
        return []
    }

其中context 和 app即为数据库相关的定义在AppDelegate中的全局变量获取方法如下:

        let app = UIApplication.shared.delegate as! AppDelegate
        let context = app.persistentContainer.viewContext

5.3、plist数据写入数据库

完整代码如下:

        //解析plist文件数据
        let plistPath : String = Bundle.main.path(forResource: "datalist.plist", ofType:nil)!
        let plistArray : NSArray = NSArray(contentsOfFile: plistPath)!
        for item in plistArray {
            let itemDict: [String: Any] = item as! [String : Any]
            let name : String = itemDict["name"] as! String
            let age : Int16 = itemDict["age"] as! Int16
            let isProtagonist : Bool = itemDict["isProtagonist"] as! Bool
            let remark : String? = itemDict["remark"] as? String
            let cartoon : String = itemDict["cartoon"] as! String
            var cartoonItem : Cartoon!
            //插入Cartoon表之前需要先查询下是否已经存在,存在不需要再插入
            var cararray = queryCartoon(name: cartoon)
            if cararray.isEmpty {
                cartoonItem = saveCartoon(name: cartoon)
            } else {
                cartoonItem = cararray[0]
            }
            //Cartoon对象获取到之后可以插入Character表
            let character = saveCharacter(name: name,
                                          age: age,
                                          isProtagonist: isProtagonist,
                                          cartoon_name: cartoonItem,
                                          remark: remark)
            //Character表插入完成之后,需要给Cartoon设置关联关系。
            cartoonItem.addToRoles(character)
        }

5.4、带条件查询

查询相关方法如下:

    /// 分页查询
    ///
    /// - Parameters:
    ///   - pageSize: 每一页条数
    ///   - currentPage: 当前页
    /// - Returns: 查询结果,两个参数同时为空,查询全部数据
    func queryCharacter(pageSize : Int? = nil, currentPage : Int? = nil) ->[Character] {
        //声明数据的请求
        let fetchRequest = NSFetchRequest<Character>(entityName : entity_Character)
        if pageSize != nil && currentPage != nil {
            fetchRequest.fetchLimit = pageSize! //限定查询结果的数量
            fetchRequest.fetchOffset = (currentPage! - 1) * pageSize!
        } else {
             fetchRequest.fetchOffset = 0 //查询的偏移量
        }
        //查询操作
        do {
            let fetchedobjects = try context!.fetch(fetchRequest)
            print(String.init(format:"%@.count = %i", entity_Character, fetchedobjects.count))
            return fetchedobjects
        }
        catch {
            fatalError("查询异常“:\(error)")
        }
        
        return []
    }
    /// 设置查询条件
    ///
    /// - Parameter predicate: 查询条件
    /// - Returns: 查询结果
    func queryCharacter(predicate : nspredicate) ->[Character] {
        //声明数据的请求
        let fetchRequest = NSFetchRequest<Character>(entityName : entity_Character)
        fetchRequest.fetchOffset = 0 //查询的偏移量
        fetchRequest.predicate = predicate
        
        //查询操作
        do {
            let fetchedobjects = try context!.fetch(fetchRequest)
            print(String.init(format:"%@.count = %i", entity_Character, fetchedobjects.count))
            return fetchedobjects
        }
        catch {
            fatalError("查询异常“:\(error)")
        }
        
        return []
    }
    
    /// 对查询结果排序
    ///
    /// - Parameter sort: 排序规则
    /// - Returns: 查询结果
    func queryCharacter(sort : [NSSortDescriptor]) ->[Character] {
        //声明数据的请求
        let fetchRequest = NSFetchRequest<Character>(entityName : entity_Character)
        fetchRequest.fetchOffset = 0 //查询的偏移量
        fetchRequest.sortDescriptors = sort
        //查询操作
        do {
            let fetchedobjects = try context!.fetch(fetchRequest)
            print(String.init(format:"%@.count = %i", entity_Character, fetchedobjects.count))
            return fetchedobjects
        }
        catch {
            fatalError("查询异常“:\(error)")
        }
        
        return []
    }

设置对应的查询条件

5.4.1、条件查询

//批量查询查询一个字段在一个数据范围内
let predicate = nspredicate(format: "name IN %@", ["佩奇", "乐迪"])
//精确查询查询一个字段等于特定值
let predicate = nspredicate(format: "age == %@", NSNumber(value: 4))
//模糊查询
//查询角色名称包含“小“或“猪”的记录,*号表示通配符
let predicate = nspredicate(format: "name CONTAINS %@ OR name like %@", "小", "*猪*")
//查询角色名称以猪开头的记录
let predicate = nspredicate(format: "name BEGINSWITH %@", "猪")
//查询角色名称以”迪“结尾的记录
let predicate = nspredicate(format: "name ENDSWITH %@", "迪")
//条件查询
//查询年龄大于18的记录
let predicate = nspredicate(format: "age > %@", NSNumber(value: 18))
//查询年龄介于5-30之间的
let predicate = nspredicate(format: "age BETWEEN {%@, %@}", NSNumber(value: 5),NSNumber(value: 30))

5.4.2、对结果排序

//名称降序
let nameSort = NSSortDescriptor.init(key: "name", ascending: false)
//名称升序
let nameSort = NSSortDescriptor.init(key: "name", ascending: true)
//年龄降序
let ageSort = NSSortDescriptor.init(key: "age", ascending: false)
//年龄升序
let ageSort = NSSortDescriptor.init(key: "age", ascending: true)
let array : [NSSortDescriptor] = []
array.append(ageSort)
array.append(nameSort)
queryCharacter(sort: array)

排序可以组合使用,但是需要注意先添加进数组的排序规则会优先满足,比如这里会先按年龄排序再按名称排序

5.3、数据库修改删除

    /// 删除删除Cartoon表中所有数据.
    // 因为设置了级联删除,Character表中的所有数据也会被删除
    func deleteObjects() {
        let array = queryCartoon()
        for item in array {
            context?.delete(item)
        }
        app?.saveContext()
    }
    
    /// 删除数据库表中所有数据,该方法慎用
    func deleteObjects() {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "aaa")
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        do {
            try getContext().execute(deleteRequest)
        } catch let error as NSError {
            print(error)
        }
    }
    /// 更新Character表数据
    ///
    /// - Parameters:
    ///   - name: 姓名,主键
    ///   - age: 年龄,修改项
    /// - Returns: 更新结果
    func updateCharacter(name: String, age: Int16) -> Bool {
        let predicate = nspredicate(format: "name == %@", name)
        let itemArr = queryCharacter(predicate: predicate)
        if itemArr.isEmpty {
            return false
        }
        let item = itemArr[0]
        item.age = age
        app?.saveContext()
        return true
    }

6、案例源码

6.1、界面展示

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

6.2、下载地址

CSDN下载地址:
github源码地址:点击跳转

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐