[iOS] 로컬 데이터 - CoreData

Sehee·2024년 5월 10일

iOS 개발하기

목록 보기
4/16
post-thumbnail

시작하면서

앱에 데이터를 저장하는 과정은 필요하다
우선은 서버를 별도로 두지 않을 예정이기 때문에, 데이터를 저장해둘 공간이 없다면 앱을 실행할 때마다 데이터가 초기화될 것이다

iOS 앱에 데이터 저장하는 방법

iOS에는 앱에 데이터를 저장하는 방법이 꽤나 여러가지인 것 같다
간단하게 알아보자

UserDefaults
사용자의 기본 설정 정보를 저장하는 데이터베이스
예 : 앱 내 설정에 활용하기 좋음 (언어, 알림 설정 등)

Keychain
데이터를 저장하는 암호화된 데이터베이스
예 : 비밀번호, 인증서처럼 타인에게 노출이 되면 안되는 (보안이 필요한) 데이터에 주로 활용

FileManager
File System의 컨텐츠에 대한 인터페이스
앱의 파일을 읽고 쓸 수 있도록 해줌

SQLite
앱에서 쓰이는 가장 표준적인 데이터베이스
데이터의 양이 많고 잘 구조화되어있는 경우 용이

CoreData
넓은 의미에서는 앱의 모델 계층이자,
데이터베이스, ORM 등의 기능을 가진 객체 그래프 관리 프레임워크
속도가 빠르고, Widget 등을 개발할때 데이터 연동 편리
참고한 글 바로가기

CoreData

객체를 저장해둘 데이터베이스가 필요한 것이니, SQLite와 CoreData 중 선택하면 될 것 같다
위젯도 한 번 건드려볼 계획이기도 하고, CoreData도 SQLite를 사용하여 데이터를 저장한다고 하니, 이참에 CoreData를 써보기로 결정!

객체 설계

데이터 저장 방법을 정하는 것도 중요하지만, 설계도 중요한 단계이다
MVP 먼저 만들 예정이니만큼 간단하게 필요한 데이터만 넣었다

링크 객체

이름타입설명
idUUID()기본키
titleString사용자 지정 제목
urlString메타데이터에서의 url
url_typeString메타데이터에서 icon / image 중 어떤 이미지로 보여줄지
(값 : icon, image 2가지)
category_idUUID()외래키 (카테고리 객체의 기본키)

카테고리 객체

이름타입설명
idUUID()기본키
titleString카테고리 이름
imojiString카테고리 아이콘 (기본 이모지 활용)

일단 이정도면 된 것 같다
뭐에 대한 객체인지 궁금하다면, 지난 포스팅을 보면 이해할 수 있을지도?

지난 포스팅 : [iOS] URL 미리보기뷰 커스텀 - LinkPresentation #2

객체 생성

프로젝트를 만들 때 Use Core Data를 이미 체크해두었다
바로 객체를 만들어보자
ios_coredata_객체_생성


데이터 저장해보기

기본적으로 프로젝트 생성할 때 CoreData를 추가하면 PersistenceController로 예제 코드가 생성되어 있다
그러나, 따로 DataController를 생성하여 코드를 적어봤다

참고한 글 바로가기

01. 라이브러리 import & init()

필요한 라이브러리는 다음과 같다

import Foundation
import CoreData

DataController라는 클래스에 init()을 적어주자
PersistenceController 예제 코드에 있던 것과 비슷하게 적어줬다

class DataController: ObservableObject {
	static let shared = DataController()
    let container: NSPersistentContainer
    
    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "WebArchive")
        if inMemory {
            container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
        }

        container.loadPersistentStores { descriptions, error in
            if let error = error {
                fatalError("failed to load data store \(error)")
            }

        }
    }
}

02. 로컬 데이터에 저장하는 메소드

객체에 데이터를 create, edit할 때 공통으로? 사용할 메소드이다

class DataController: ObservableObject {
	init(inMemory: Bool = false) { ... }
    
    // 로컬 데이터에 저장
	func save(context: NSManagedObjectContext) {
        do {
            try context.save()
            print("DATA SAVED")
        } catch {
            print("FAILED TO SAVE DATA")
        }
    }
}

03. 객체에 데이터를 Create & Update

class DataController: ObservableObject {
	init(inMemory: Bool = false) { ... }
    
    // 로컬 데이터에 저장
	func save(context: NSManagedObjectContext) { ... }
    
    // MARK: - Link Data
    func addLink(title: String, url: String, url_type: String, context: NSManagedObjectContext) {
        let link = Link(context: context)
        link.id = UUID()
        link.title = title
        link.url = url
        link.url_type = url_type
        
        save(context: context)
    }
    
    func editLink(link: Link, title: String, url: String, url_type: String, context: NSManagedObjectContext) {
        link.title = title
        link.url = url
        link.url_type = url_type
        
        save(context: context)
    }
}

03. 뷰에 연결

이 부분에서 한참 헤맸었는데, 기존 persistance 예제가 연결되어있던 것 처럼 똑같이 연결하면 된다

@main
struct WebArchiveApp: App {
    @StateObject var dataController =  DataController.shared

    var body: some Scene {
        WindowGroup {
            URLPreviewView() // LP 링크 프리뷰 커스텀한 뷰
                .environment(\.managedObjectContext, dataController.container.viewContext)

        }
    }
}

App 파일에 DataController를 Environment를 통해 모든 뷰에 전해준다

04. 오류 해결

Thread 1: "executeFetchRequest:error: A fetch request must have an entity."
오류가 계속 떠서 이것저것 계속 구조 변경해보면서 뷰를 연결했었는데,
다른 오류들은 처음 persistance 예제를 참고하면 해결되는 것들이었다

이제 마지막에 DataController에 @MainActor 추가해주니까 해결되었다

참고한 글 바로가기

@MainActor
class DataController: ObservableObject { ... }

05. 데이터 추가해보기

대충 폼 형태 만들어서 데이터가 잘 추가되는지만 확인해봤다

struct URLPreviewView: View {

    // DataController를 활용할 뷰에 데이터 가져오기
    @Environment(\.managedObjectContext) var manageObjContext
    @FetchRequest(sortDescriptors: []) var link: FetchedResults<Link>

    // 생략
    
    var body: some View {
        // 생략
        Button("링크 추가하기", action: {
            DataController()
                .addLink(
                    title: inputTitle,
                    url: inputURL,
                    url_type: selectedSide == .icon ? "icon" : "content",
                    context: manageObjContext)
        })
    }
}

ios_coredata_데이터_추가하기

텍스트필드에 url과 title(선택)을 입력하면 데이터가 추가되어 아래 리스트뷰에 보여지는 형태이다
잘 동작한다 뿌듯


그리고...

이제는 더이상 디자인을 미룰 수 없다
CoreData는 1차적으로 연결은 해뒀으니, 뷰를 먼저 그린 후 기능을 추가하는 방향으로 개발해야할 것 같다


참고한 글 (본문 삽입 외)
https://velog.io/@nala/iOS-SwiftUI에서-CoreData-써보기
https://velog.io/@leeesangheee/Core-Data-사용해-CRUD-구현하기

profile
디자인하는 개발자

0개의 댓글