포스트

7주차 과제 (3)

CoreData 사용하기

이번 과제에서는 CoreData를 사용하는것이 있기에 그부분을 구현하려한다.

이미 DataModel라는 API로 부터 가져오는 똑같은 이름이 있으니

LocalModel로 만들어 주었다.

Entities 가 Table

Attributes 가 Field라고 보면 된다.

CleanShot 2024-04-10 at 17 08 30@2x

이렇게 만들어 주었다.

사용전 AppDelegate에 다음과 같이 내용을 적어주자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import CoreData

// MARK: - Core Data stack

    lazy var persistentContainer: NSPersistentContainer = {
        /*
         The persistent container for the application. This implementation
         creates and returns a container, having loaded the store for the
         application to it. This property is optional since there are legitimate
         error conditions that could cause the creation of the store to fail.
        */
        let container = NSPersistentContainer(name: "LocalModel")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                 
                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                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 {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

그리고 Container와 소통할 Context를 적어준다.

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

앞으로 CRUD에 관한 모든것들은 Context를 통해서 이루어지게될것이다.

그리고 버튼을 눌렀을때 CoreData에 들어가야 하기에 다음과 같이 구현해준다.

1. Create 구현.

CRUD중 첫번째인 C를 구현한다.

값을 그냥 DB에 추가를 하면 되는 부분이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@IBAction func saveListBtn(_ sender: UIButton) {
        
    
        let newItem = Lists(context: self.context)
        
        newItem.id = Int64(list[0].id)
        newItem.title = list[0].title
        newItem.price = Int64(list[0].price)
        newItem.discountPercentage = list[0].discountPercentage
        
        do
            {
               try context.save()
                
            } catch {
                print(error.localizedDescription)
            }
    }

CleanShot 2024-04-10 at 22 58 38@2x

2. Read 구현

두번째인 R을 구현한다.

1. TableVC 생성

해당 부분은 TableView에 보여져야 하므로, 새로운 VC를 만들어야한다.

디자인과는 무관하므로 TableViewController 통으로 된걸 만들었다.

1
2
3
4
5
6
7
@IBAction func showDBBtn(_ sender: UIButton) {
        
        if let tableVC = self.storyboard?.instantiateViewController(identifier: Constansts.tableVC) as? DBTableViewController {
            
            self.present(tableVC, animated: true)
        }
    }

그리고 다음과 같이 구현했다.

이렇게하면 화면전환은 문제없이 된다.

2. TableVC 내용 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class DBTableViewController: UITableViewController {
    
    var savedList: [Lists] = [Lists]()
    
    override func viewDidLoad() {
        super.viewDidLoad()

     
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return savedList.count
    }
    
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: Constansts.cellIdentifier) else {
            return UITableViewCell()
        }
        
        cell.textLabel?.text = "\(savedList[indexPath.row].id)\(savedList[indexPath.row].title ?? "None")"
        
        return cell
    }


}

이렇게 구성을 해두었다.

context가 VC에 있기에 거기서 로드를 하여 배열에 저장 후, property를 통해 직접 전달로 하면서 넘기면 될 듯 하다.

3. Context 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@IBAction func showDBBtn(_ sender: UIButton) {
        
        let request : NSFetchRequest<Lists> = Lists.fetchRequest()
        
        do {
            savedList = try context.fetch(request)
        } catch {
            print(error.localizedDescription)
        }
        
        
        if let tableVC = self.storyboard?.instantiateViewController(identifier: Constansts.tableVC) as? DBTableViewController {
            
            tableVC.savedList = savedList
            
            self.present(tableVC, animated: true)
        }
        
    }

request를 이용한다.

작성하는 방식은 3가지가 존재한다

1
2
3
4
5
let request = NSFetchRequest<Entity>(entityName: "Entity")

let request: NSFetchRequest<Entity> = Entity.fetchRequest()

let request = Entity.fetchRequest()
  1. 직접 생성
    • NSFetchRequest를 직접 생성하고 엔터티의 이름을 지정하여 검색 요청을 정의
    • entityName 매개변수에는 검색할 엔터티의 이름을 String으로 전달 엔터티 이름을 하드 코딩하는 방식으로 해당 방법은 지양
  2. 자동 생성
    • Core Data에서 자동으로 생성한 Swift 클래스의 fetchRequest() 메서드를 사용하는 방식
    • 엔터티 이름 하드 코딩할 필요없이 검색 요청 정의 가능
    • NSFetchRequest라는 정확한 타입 정보를 가지고 있음
  3. 자동 생성 단축형
    • 2번의 방법과 동일하며, 타입 정보를 생략, 코드의 가독성을 높인 간결한 방식이다.
    • 엔터티 타입을 정확하게 추론한다.

참고사이트

그리고 가격부분을 좀 다듬어 주었다.

1
2
3
4
5
6
7
8
9
10
11
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        guard let cell = tableView.dequeueReusableCell(withIdentifier: Constansts.cellIdentifier) else {
            return UITableViewCell()
        }
        
        let price = Double(savedList[indexPath.row].price) * (100.00 - savedList[indexPath.row].discountPercentage)
        cell.textLabel?.text = "[\(savedList[indexPath.row].id)] \(savedList[indexPath.row].title ?? "None") - \(numberFormatter.string(from: price as NSNumber) ?? "0")$"

        return cell
    }

CleanShot 2024-04-11 at 00 21 04@2x

잘나온다.

좀 짤리는 부분이 있어서 폰트 사이즈를 조정해준다. cell.textLabel?.font = UIFont.systemFont(ofSize: 15)

simulator_screenshot_3DB20982-F49D-4744-AF83-CDFA908D261B

Lv.3 까지 끝.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.