본문 바로가기

개발/Swift

[Swift/Realm] List-LinkingObject 로 관계 설정한 데이터 Read, Create, Update 하기.

Angela Swift 부트캠프를 들으면서 가장 어려웠던 부분이라 따로 정리하게 되었다.
정말 이것 때문에 얼마나 씨름했는지.. 절대 까먹기 싫어서 정리하려고 한다.

만약 틀린 부분이 있다면 지적해주시면 너무 감사드리겠습니다..!!

아래는 Object 를 상속하는 클래스를 List와 LinkingObject로 관계를 설정했다.

//
// Data.swift
//

import Foundation
import RealmSwift

class Category: Object {
    @objc dynamic var name: String = ""
    var items = List<Item>() 
        // items 프로퍼티는 1:다 관계를 정의한 것이다. 
        // Item Object의 parentCategory와 연결된다.
}

class Item: Object {
    @objc dynamic var title: String = ""
    @objc dynamic var dayCreated: Date?
    @objc dynamic var check: Bool = false
    var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
        // parentCategory 는 1:1관계로 이전 관계의 프로퍼티를 링크하는 것이다. 
        // items라는 프로퍼티로 연결되기 때문에 "items"로 기입한다.
}

@objc dynamic을 @Persisted로 정의하면 앱이 크러시된다.

primary key를 정의하지 않아서 그런가? 이것은 더 공부해봐야할 사항이다.

ItemViewController 에서의 관계설정은 이렇게 한다.

  • 어떤 카테고리가 선택되어 이 ViewController로 오게 된것인지 데이터를 받는다.
  • 데이터가 셋팅되면 데이터를 로드하는 메소드를 호출한다.
//
// ItemViewController.swift
//

var itemArray : Results<Item>?

// CategoryViewConroller에서 선택한 카테고리가 무엇인지 데이터를 전달받는 부분
var selectedCategory: Category? {
    didSet {
        loadData()
    }
}

CategoryViewConroller에서는 카테고리를 선택했을 때 선택한 카테고리가 무엇인지 ItemViewController로 전달한다.

//
// CategoryViewConroller.swift
//

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let destinationVC = segue.destination as! TableViewController
        // 아래부분이 선택된 카테고리에 대한 indexPath.row 데이터를 다음 ItemViewController로 전달하는 부분.
        if let indexPath = tableView.indexPathForSelectedRow {
            destinationVC.selectedCategory = categoryArray?[indexPath.row]
        }
    }

이제 유의해야할 중요한 것이 있다.

ItemViewController에서 데이터를 로드하고, 저장하는 방법이 기존방법과 다르다.

selectedCategory 프로퍼티의 items의 데이터를 가져오고 title 기준으로 정렬한다.

//
// ItemViewController.swift
//

// 아래 방법으로 데이터를 로드한다.
func loadData() {
        itemArray = selectedCategory?.items.sorted(byKeyPath: "title")
        tableView.reloadData()
    }

새 object를 만들때는 꼭 realm.write 코드블럭 안에서 생성하고 추가해야한다.

추가는 realm.add 로 하는 것이 아닌 selectedCategory.items.append(newItem) 로 추가해야

해당하는 카테고리에 추가된다.

만약 Coffee를 선택하여 들어왔다면 Coffee 카테고리의 items 프로퍼티에 Object를 추가하는 것이다.

realm.add(newItem)으로 추가한다면 Coffee 라는 카테고리에 추가하는 것이 아니기에 아무리 추가해도 보이지 않는다.

(loadData()에서 realm.object(Item)이 아닌 selectedCategory?.items에 대한 데이터를 보여주기 때문이다.)

//
// ItemViewController.swift
//

// 새로운 object를 생성하는 것은 아래와 같이 realm.write 안에서 오브젝트를 만들어야 한다.
var textField = UITextField()
if let selectedCategory = self.selectedCategory {
    do {
        try self.realm.write {
            // 이 안에서 object를 생성하지 않으면 앱이 crash 되며 아래문구가 뜬다.
            // cannot modify managed rlmarray outside of a write transaction.
            let newItem = TodoListModel()
            newItem.title = textField.text!
            newItem.check = false
            newItem.dayCreated = Date()
            selectedCategory.items.append(newItem)
                        tableView.reloadData()
        }
    } catch {
        print("save error \(error)")
    }
}

관계를 뷰로 표현하자면 이렇게 표현할 수 있을 것같다.

Realm Studio로 본 DB구조

Coffee에서 생성하면 Category Coffee의 items 배열에 추가된다.

Item 클래스에는 Category에 상관없이 추가되어있는 것을 볼 수 있다.

Item 클래스인 것이 모두 추가되고, Category클래스의 items에는 관계가 결정되어지는 것 같다.

 

 

Reference

- iOS & Swift - The Complete iOS App Development Bootcamp by Dr. Angela Yu (Udemy)

 

 

 

반응형