Focus On Develop ๐ŸคŸ๐ŸคŸ

[Swift] Core Data(2) ์‹ค์Šตํ•ด๋ณด๊ธฐ ๋ณธ๋ฌธ

iOS [Swift]/๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!

[Swift] Core Data(2) ์‹ค์Šตํ•ด๋ณด๊ธฐ

๋ˆ„๋ฆฌ๋‹ฌ์ดํ‹€ 2020. 12. 21. 16:33

Core Data ๊ฐœ๋… ๋จผ์ € ์•Œ์•„๋ณด๊ณ  ์˜ค๊ธฐ!! ๐Ÿ‘‰  2020/12/21 - [iOS [Swift]/๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!] - [Swift] Core Data(1) ๊ฐœ๋…์ •๋ฆฌ


STEP 1. Core Data, Core Data Model ๋งŒ๋“ค๊ธฐ

 

์‹ ๊ทœ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์ƒ์„ฑ์‹œ Use Core Data๋ฅผ ์„ ํƒํ•ด์ค์‹œ๋‹ค. ๋งŒ์•ฝ ๊ธฐ์กด ํ”„๋กœ์ ํŠธ์— Core Data๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, Core Data ํ•˜์œ„์˜ Data Model ํŒŒ์ผ์„ ์„ ํƒํ•ด์„œ ์ƒ์„ฑํ•ด์ฃผ๋ฉด ๋˜์š”.

 

๊ทธ๋Ÿผ ์ขŒ์ธก์— ํŒŒ์ผ ๋ชฉ๋ก์„ ๋ณด๋ฉด ์ด๋ ‡๊ฒŒ .xcdatamodeld ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์—ˆ์„ ๊ฑฐ์—์š”.

 

์ด์ œ ์—ฌ๊ธฐ์„œ ๋ชจ๋ธ์„ ์ƒ์„ฑํ•ด์ฃผ๋ฉด ๋˜์š” ใ…Žใ…Ž ์ขŒ์ธก ํ•˜๋‹จ์— Add Entity๋ฅผ ํ†ตํ•ด Entity๋ฅผ ์ƒ์„ฑํ•˜๊ณ  (Database์—์„œ ํ…Œ์ด๋ธ” ์ด๋ผ๊ณ  ๋น„์œ ํ•ด์„œ ์ƒ๊ฐํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Œ) Add Attribute๋˜๋Š” '+' ๋ฒ„ํŠผ์„ ํ†ตํ•ด Attribute๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๋ฉด ๋˜์š”. (Database์—์„œ ์ปฌ๋Ÿผ์œผ๋กœ ๋น„์œ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Œ)

 

ํƒ€์ž…์„ ์ง€์ •ํ• ๋•Œ Custom Type์œผ๋กœ ์ •์˜ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, Type์„ Transformable๋กœ ์ง€์ •ํ•˜๊ณ  Attribute Inspector์—์„œ Custom Class๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋˜์š”. ex) [String], [String: Any] ๋“ฑ

 

๊ทธ๋ฆฌ๊ณ  Entity ์„ค์ •์‹œ ์šฐ์ธก Inspector๋ฅผ ๋ณด๋ฉด Codegen์ด๋ผ๋Š” ์˜์—ญ์ด ์žˆ๋Š”๋ฐ, ์ด ๋ถ€๋ถ„์€ ์žฅ/๋‹จ์ ์ด ๊ฐ๊ฐ์žˆ์œผ๋‹ˆ ์ž…๋ง›์— ๋งž๋Š” ๊ฒƒ์„ ์„ ํƒํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ใ…‹ใ…‹ ( ๋‚˜๋Š” ๊ฐœ์ธ์ ์œผ๋กœ Manual/None์ด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.. ๐Ÿ’ )

 

  • Manual/None
    • Access Modifiers๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , method๋‚˜ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด managed object subclass๋ฅผ Customizingํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ
    • model์„ ๋ณ€๊ฒฝ๋˜์„œ ์ƒˆ๋กœ Generating ํ• ๋•Œ๋งˆ๋‹ค ๊ธฐ์กด model์„ ๊ฐฑ์‹ ํ•ด์ค˜์•ผํ•จ
  • Class Definition
    • Core Data๊ฐ€ ์ƒ์„ฑํ•œ managed object subclass ๋ฐ property, ๊ธฐ๋Šฅ์„ Customizing ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ
    • ์ฆ‰ XCode์—์„œ ์•Œ์•„์„œ ์ฒ™์ฒ™ ํ•ด์ค€๋‹ค๋Š” ์˜๋ฏธ์ด๊ณ , ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์†Œ์Šค ๋ชฉ๋ก์— ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋Š”๋‹ค
    • SubClass๋ฅผ ์‚ญ์ œํ•˜๋”๋ผ๋„ ๋ˆˆ์—๋Š” ์•ˆ๋ณด์ด์ง€๋งŒ ๋‚จ์•„์žˆ์Œ.. (๋‚˜์ค‘์— ํŒŒ์ผ์ด ๊ผฌ์ผ ์ˆ˜ ์žˆ์Œ)
    • model์ด ๋ณ€๊ฒฝ๋˜๋„ Generating์‹œ ์•Œ์•„์„œ ์ฒ™์ฒ™ ํ•ด์คŒ
  • Category/Extension : 
    • managed object subclass์— method๋‚˜ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์„๋•Œ
    • ํด๋ž˜์Šค ํŒŒ์ผ์„ ์™„์ „ํžˆ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์œผ๋‚˜ ๋ชจ๋‘ ์ˆ˜๋™์œผ๋กœ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•ด์ค˜์•ผ ํ•จ

์ด์ œ ๊ธฐ๋ณธ์ ์ธ ์„ค์ •์„ ์™„๋ฃŒํ–ˆ์œผ๋ฉด, Model์„ Generating ํ•˜๋ฉด ๋˜์š”. Editor > Create NSManagedObject Subclass

(Relationship์€ ์ง€๊ธˆ์€ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ํ•ด๋ณด๋Š” ๊ฑฐ๋ผ์„œ ๋‚˜์ค‘์— ๊ณต๋ถ€ํ•ด๋ณผ๊ฑฐ๋‹ค.. ๐Ÿ˜‚)

 

์งœ์ž”~ ์ด๋ ‡๊ฒŒ ํ•˜๊ณ  ๋‚˜๋ฉด ๋‚ด๊ฐ€ ๋ฐฉ๊ธˆ ๋งŒ๋“ค์—ˆ๋˜ Entity์— ๋Œ€ํ•ด Model File์ด ์ถ”๊ฐ€๋œ๋‹ต๋‹ˆ๋‹ค~~

 

์—ฌ๊ธฐ์„œ ๋ˆˆ์—ฌ๊ฒจ ๋ณด๋ฉด ์ข‹์€๊ฑด, Property ํŒŒ์ผ์— ์•„๋ž˜์ฒ˜๋Ÿผ fetchRequest๊ฐ€ ์ƒ๊ธฐ๋Š”๋ฐ, ์ด method๋ฅผ ํ†ตํ•ด์„œ Data๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๊ฒƒ์ด๋‹ˆ ๊ธฐ์–ตํ•ด๋‘์ฃ !

extension Todo {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Todo> {
        return NSFetchRequest<Todo>(entityName: "Todo")
    }

    @NSManaged public var contents: String?
    @NSManaged public var date: Date?
    @NSManaged public var id: Int64
    @NSManaged public var priority: Int64

}

 

STEP 2. ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

์•ฑ์— Core Data๋ฅผ ์ ์šฉํ•˜๊ณ  ๊ฐ„๋‹จํ•œ Model์„ ์ƒ์„ฑํ•ด๋ดค์–ด์š”. ๋ฒŒ์จ ์ ˆ๋ฐ˜์€ ํ–ˆ๊ตฐ์š”..!? ์‹œ์ž‘์ด ๋ฐ˜์ด๋‹ค ใ…‹ใ…‹ ๐ŸคŸ๐ŸคŸ

CoreData๋ฅผ ์ ์šฉํ•˜๋ฉด AppDelegate์— persistentContainer, saveContext()๊ฐ€ ์ƒ๊ธฐ๋Š”๋ฐ์š”~! ์š”๊ธฐ์„œ ์ƒ์„ฑ๋œ persistentContainer๊ฐ€ ๊ฐœ๋… ์ •๋ฆฌ์—์„œ ์‚ดํŽด๋ณด์•˜๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ์šฐ๋ฆฌ๊ฐ€ ์ €์žฅํ•˜๋Š” Data๊ฐ€ ๋‹ด๊ธฐ๋Š” ์ €์žฅ์†Œ๋ผ๊ณ  ๋ณด๋ฉด ๋  ๊ฒƒ ๊ฐ™๊ณ , saveContext๋Š” ๋ง๊ทธ๋Œ€๋กœ ๊ธฐ์กด ๋ฐ์ดํ„ฐ์™€ ๋น„๊ตํ–ˆ์„๋•Œ ๋ณ€ํ™”๊ฐ€ ์žˆ์œผ๋ฉด ์‹ค์ œ๋กœ ์ €์žฅํ•˜๋Š” method๋ผ๊ณ  ๋ณด๋ฉด ๋  ๊ฒƒ ๊ฐ™์•„์š”.

 

์ด์ œ ViewController๋กœ ๊ฐ€์„œ, persistentContainer๋กœ ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ NSManagedObjectContext๋ฅผ ์ƒ์„ฑํ•ด์ค์‹œ๋‹ค!

// Reference to managed object context
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

์ด์ œ persistentContainer๋กœ ์ ‘๊ทผํ•ด์„œ CRUD๋ฅผ ์œ„ํ•ด, ์—ฌ๊ธฐ์„œ ์ •์˜ํ•œ context๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋„ค์š”~!! ์•ผํ˜ธ ๐Ÿ˜

 

์œ„์—์„œ ์„ค๋ช…ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ, ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•ด์„œ ๋ชจ๋ธ Generating์‹œ ์ƒ์„ฑ๋œ fetchRequest๋ฅผ ํ˜ธ์ถœํ•ด์ค์‹œ๋‹ค~! ํ˜ธ์ถœ๋œ ๊ฒฐ๊ณผ๋ฅผ context.fetch()๋ฅผ ํ†ตํ•ด ์‹ค์ œ ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ์— ๋‹ด์•„์ฃผ๊ณ ์š”~!!

(์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ items์— ๋‹ด๊ณ , tableView ๊ฐฑ์‹ ํ•ด์ฃผ๊ธฐ! ์ €๋Š” ๊ณต๋ถ€๋ฅผ ์œ„ํ•ด ๋งŒ์ธ์˜ tableView๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋  ใ…‹ใ…‹)

    func fetchTodo() {
        do {
            let request = Todo.fetchRequest() as NSFetchRequest<Todo>
            self.items = try context.fetch(request)

            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        } catch let error {
            print("---> \(error.localizedDescription)")
        }
        
    }

 

๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์œผ๋‹ˆ, ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฑด ์–ด๋–ป๊ฒŒ ํ• ๊นŒ~~? ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ๋ณด์ž. ์‘..?? ๋‚ด๊ฐ€ ์›๋ž˜ ๋”ฐ๋กœ ๋ชจ๋ธ ์ •์˜ํ•ด์„œ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ๊ณผ ๊ฑฐ์˜ ๋™์ผํ•˜๋„ค..!?!? ๋งž์Šต๋‹ˆ๋‹ค!! ๋Œ€์‹ , initialize์— ์œ„์—์„œ ์ •์˜ํ•œ ํ˜„์žฌ context๋ฅผ ๋„ฃ์–ด์ค์‹œ๋‹ค! ๊ทธ๋ฆฌ๊ณ  ๊ฐ Attribute๋Š” ์นœ์ ˆํ•˜๊ฒŒ ์ฑ„์›Œ์ฃผ๋„๋ก ํ•ฉ์‹œ๋‹ค~!! ํ‘ธํ•ซ ๐Ÿ˜Ž๐Ÿ˜Ž ์ €์žฅ์€ context.save() method๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋œ๋‹ต๋‹ˆ๋‹ค~~ ์ €์žฅ ํ›„์—๋Š” ์ƒˆ๋กœ ํ•œ๋ฒˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์•ผ๊ฒ ์ฃ ~~?

// create a todo object
let newTodo = Todo(context: self.context)
newTodo.contents = textfield.text
newTodo.date = Date()
newTodo.id = 1
newTodo.priority = 1
            
// save the data
do {
    try self.context.save()
} catch let error {
    print("save error ---> \(error.localizedDescription)")
}
            
// refetch the data
self.fetchTodo()

 

STEP 3. ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ๋ฐ ์‚ญ์ œ

๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, ๋จผ์ € ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ• ์ง€ ์„ ํƒํ•ด์•ผ๊ฒ ์ฃ ~~? (์ €๋Š” tableView๋กœ ์‹ค์Šตํ•ด์„œ items[indexPath.row]๋กœ ์„ ํƒํ–ˆ๋‹ต๋‹ˆ๋‹ค~ ์„ ํƒ๋œ todo.contents๋ฅผ ์•„๋ž˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋ณ€๊ฒฝํ•ด์ฃผ๋ฉด ๋˜์š”! ์—…๋ฐ์ดํŠธ ํ›„ ์ €์žฅ, ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ๋ถˆ๋Ÿฌ์™€์„œ ํ‘œ์‹œํ•ด์ฃผ๋Š” ๊ฑด ๊ฐฑ์‹ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ํ•„์ˆ˜~!! ๐Ÿ˜Ž

let todo = self.items[indexPath.row]
 ...
            
// edit contents property of todo object
todo.contents = textField.text
            
// save the data
do {
    try self.context.save()
} catch let error {
    print("save error ---> \(error.localizedDescription)")
}
            
// refresh the data
self.fetchTodo()

 

์‹ค์Šตํ•œ ๊ฒƒ ์ค‘์— ์ œ~~์ผ ์‰ฌ์šด๊ฒŒ ๋ฐ์ดํ„ฐ ์‚ญ์ œ์—์š”. ์—…๋ฐ์ดํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์‚ญ์ œํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์„ ํƒํ•œ ๋’ค์—, context.delete() method๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด ๋œ๋‹ต๋‹ˆ๋‹ค~ ์—ญ์‹œ ์‚ญ์ œ ํ›„์—๋„ ๋ฐ์ดํ„ฐ ์ €์žฅ๊ณผ ๊ฐฑ์‹ ์€ ํ•„์ˆ˜! ๐Ÿ˜Ž

let todoToRemove = self.items[indexPath.row]
 ...
            
// remove the todo
self.context.delete(todoToRemove)
            
// save the data
do {
    try self.context.save()
} catch let error {
    print("save error ---> \(error.localizedDescription)")
}
            
// refresh the data
self.fetchTodo()

 

์ด๋ ‡๊ฒŒ ์šฐ๋ฆฌ ๊ฐ™์ด Core Data ์ ์šฉ ๋ฐ ๋ชจ๋ธ์ƒ์„ฑ, NSManagedObjectContext๋ฅผ ํ†ตํ•œ PersistentContainer๋กœ ์ ‘๊ทผ ๋ฐ ๊ธฐ๋ณธ์ ์ธ CRUD๋ฅผ ์‹ค์Šตํ•ด๋ดค๋Š”๋ฐ์š”~!! ์ด๊นŒ์ง€ ๊ธด ์—ฌ์ • ๋”ฐ๋ผ์˜ค์‹  ๋ชจ๋“  ๋ถ„๋“ค ์ˆ˜๊ณ ํ•˜์…จ์Šต๋‹ˆ๋‹ค ๐Ÿค™๐Ÿค™ (์‹œ์ž‘๊ณผ ๋‹ค๋ฅด๊ฒŒ NS ์–ด์ฉŒ๊ตฌ ์ €์ฉŒ๊ตฌ๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋ถˆํŽธํ•˜์ง€ ์•Š๊ณ  ์•„์ฃผ ์•ฝ~๊ฐ„์€ ์ต์ˆ™ํ•ด์ง€์…จ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค ใ…‹ใ…‹  ๐Ÿ‘)

 

 

Core Data ๊ฐœ๋… ์•Œ์•„๋ณด๋Ÿฌ ๊ฐ€๊ธฐ!! ๐Ÿ‘‰  2020/12/21 - [iOS [Swift]/๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!] - [Swift] Core Data(1) ๊ฐœ๋…์ •๋ฆฌ

 

 

์ถœ์ฒ˜ ๋ฐ ์ฐธ๊ณ 

1) www.youtube.com/user/CodeWithChris

์ง„์งœ Chris๋‹˜ ๋„ˆ๋ฌด ์ข‹์€ ๊ฐ•์˜ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ™ CWC+๊ฐ•์˜๋Š” ๋„ˆ๋ฌด ๋“ฃ๊ณ  ์‹ถ์ง€๋งŒ ์•„์‰ฝ๊ฒŒ๋„ ๋น„์‹ผ๊ฑฐ๊ฐ™์•„์š”..๐Ÿ˜ญ

Thank you for good lecture Chris ๐Ÿ™ I want to take a CWC+ lectures, but it's expensive for me.. sorry.. ๐Ÿ˜ญ

But i subscribed your channel ๐Ÿ‘

Comments