본문 바로가기

프로그래밍/SwiftUI

Core Data에서 Transformable 데이터 활용하기

Swift 5

Xcode 12.2

iOS14.2

 

Application에 데이터를 저장하는 방법 중 하나로 가장 대표적인 것으로 Core Data를 뽑을 수 있다.

Core Data에서는 기본적으로 아래와 같은 Data Type을 제공한다. 

 

bit 수에 따른, Integer나, String, 심지어 UUID까지 제공해서, 기본적인 App을 만들기에는 충분하다. 하지만 때로는 자신이 원하는 Data Type이 없는 경우도 있는데, 이를 위해 Transformable Type이 존재한다. 

Transformable으로 설정을 해도 Swift에서 제공하는 기정의 된 Data Type만 사용할 수 있지만, 개인이 만든 Structure가 본래 기정의 된 Data Type을 활용하므로, 크게 문제 되지는 않을 것이다. (일반적인 Structure로 설정할 수 있는 방법을 알게 된다면, 따로 정리를 하겠다)

 

 

 

 

 

나는 이번 포스팅에서 Transformable로 String 배열을 선언하는 방법을 살펴보고자 한다. Core Data에서 제공하는 String Type이 있긴 하지만, 데이터를 한 번에 조작하기 위해서 배열이 필요할 때가 자주 있다. 

 

먼저 Xocde를 열어 Project를 하나 개설한다. 그다음 iOS의 App을 선택하면 아래와 같은 화면이 나오는데, 아래와 같이 "Use Core Data"를 선택하고 Next 버튼을 클릭한다. 그다음 창에서 Create 버튼을 클릭하면, Project가 만들어진다. 

 

프로젝트를 만들고 Navigator를 살펴보면, Core Data를 사용하지 않은 Project와 달리,. xcdatamodeld 파일과 Persistence.swift 파일이 생성되었고, ContentView.swift의 초기 코드도 복잡(?)해졌다. 

 

 

 

 

 

초기 코드를 빌드하면 아무것도 나오지 않기 때문에, 다음과 같이 List를 NavigationView로 감싸고, EditButton과 addItem Button을 toolbar에 추가해주었다. 

struct ContentView: View {
	
    ...
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
            .navigationBarTitle("Core Data Example", displayMode: .inline)
        }
    }

...

 

해당 예제는 다음과 같이, + 버튼을 누르면, 누른 시간이 리스트에 나타나며, 해당 데이터는 앱을 다시 시작해도 그대로 남아 있게 된다. 

 

다음 작업을 하기에 앞서 해당 앱은 추가되는 데이터가 에러를 발생시키므로 시뮬레이터나 아이폰에서 완전히 삭제해주어야 한다. 

 

이제 여기에 String 배열의 속성을 추가해 보자.  .xcdatamodeld파일에서 Item Entity를 클릭하여 아래와 같이 Inspectors의 Class에 있는 Codegen을 Manual/None으로 변경해 준다. Codegen을 바꿔주지 않으면 추후 빌드할 때, 파일 간 충돌이 일어나는 등 문제가 생긴다.  

 

그리고 난 다음, +버튼을 눌러 속성을 추가하고, Inspectors 창에서 아래와 같이 설정하였다.

(Name: strArray, Type: Transformable, Transformer: NSSecureUnarchiveFromData, Custom Class: [String])

참고로 Transformer에서 NSSecureUnarchiveFromData와 같은 설정을 안 해주면, 해당 속성에 접근할 때마다 "'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release"라는 경고가 뜨게 되므로, 지금 설정해주어야 한다. 

 

그리고 난 다음 Menu Bar의 Editor에 Create NSManagedObject Subclass... 를 누른다. 그리고 나타나는 창에서 자신이 만든 프로젝트를 선택하고(여기서는 CoreDataExample) Next를 누른 뒤, Item Entity를 선택하고 Next를 누르고 Create를 누르자. 

 

그러면, 아래와 같이 +CoreDataClass.swift와 +CoreDataProperties.swift 파일이 추가로 나타나게 된다. 해당 파일은 Core Data 프로젝트를 만들면 자동으로 생성되는데, 앞선 작업을 통해 편집 가능하도록 나타난 것일 뿐이다. 이 중 +CoreDataProperties.swift파일을 살펴보면, 앞에서 추가한 strArray: [String]? 도 보인다. 

여기서는 [String]에 있는 ?(Optional)는 지워야 하는데, 데이터를 업데이트할 때, unwrapping 에러가 발생한다. 

 

그다음 ContentView.swift를 다음과 같이 수정해준다. add 버튼을 누를 때, 0에서 9까지의 랜덤 값을 정해 strArray에 대입해주었고, 이를 List에 띄어주도록 수정해주었다. 

struct ContentView: View {
	...

    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    Text("Item at \(item.timestamp!, formatter: itemFormatter): \(item.strArray[0])")
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ...
            }
            .navigationBarTitle("Core Data Example", displayMode: .inline)
        }
    }

    private func addItem() {
        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
            newItem.strArray.append(String(Int.random(in: 0..<10)))
            
            ...
        }
    }

    ...
}

...

 

그리고 빌드 후, +버튼을 누르면, 아래와 같이 누른 시간 뒤에 랜덤 하게 숫자가 나온다. 이는 마찬가지로 앱을 종료하고도 그대로 남아있게 된다. 

entity에 Attributes를 추가하려면, 아래와 같이 추가한 뒤에, Editor의 Create NSManagedObject Subclass...를 눌러 +CoreDataClass.swift 파일과, +CoreDataProperties.swift 파일을 다시 생성해야 한다. 

그러면 추가한 strArray2를 포함하여, 두 파일이 덮어써진 것을 확인할 수 있는데, 이전에 strArray를 Array<String>?에서 Array<String>으로 수정해 준 것도 초기화된 것을 확인할 수 있다. 이와 같이 새로 만들어지므로 이전에 있던 코드를 잘 기억해 두어야 한다. 

그리고 한 가지 주의할 점은, Attribute를 추가하고 해당 속성을 사용한다면, 시뮬레이터나 디바이스에서 앱을 지웠다가 다시 설치를 해줘야 한다. 이전 Core Data를 기준으로 앱이 생성된 것이므로, 에러가 발생한다. 

'프로그래밍 > SwiftUI' 카테고리의 다른 글

Picker 사용시 주의점: data type  (0) 2020.12.20
[iOS] SwiftUI를 이용하여 Web page 띄우기  (0) 2020.08.09