앱에 데이터를 저장하는 과정은 필요하다
우선은 서버를 별도로 두지 않을 예정이기 때문에, 데이터를 저장해둘 공간이 없다면 앱을 실행할 때마다 데이터가 초기화될 것이다
iOS에는 앱에 데이터를 저장하는 방법이 꽤나 여러가지인 것 같다
간단하게 알아보자
UserDefaults
사용자의 기본 설정 정보를 저장하는 데이터베이스
예 : 앱 내 설정에 활용하기 좋음 (언어, 알림 설정 등)
Keychain
데이터를 저장하는 암호화된 데이터베이스
예 : 비밀번호, 인증서처럼 타인에게 노출이 되면 안되는 (보안이 필요한) 데이터에 주로 활용
FileManager
File System의 컨텐츠에 대한 인터페이스
앱의 파일을 읽고 쓸 수 있도록 해줌
SQLite
앱에서 쓰이는 가장 표준적인 데이터베이스
데이터의 양이 많고 잘 구조화되어있는 경우 용이
CoreData
넓은 의미에서는 앱의 모델 계층이자,
데이터베이스, ORM 등의 기능을 가진 객체 그래프 관리 프레임워크
속도가 빠르고, Widget 등을 개발할때 데이터 연동 편리
참고한 글 바로가기
객체를 저장해둘 데이터베이스가 필요한 것이니, SQLite와 CoreData 중 선택하면 될 것 같다
위젯도 한 번 건드려볼 계획이기도 하고, CoreData도 SQLite를 사용하여 데이터를 저장한다고 하니, 이참에 CoreData를 써보기로 결정!
데이터 저장 방법을 정하는 것도 중요하지만, 설계도 중요한 단계이다
MVP 먼저 만들 예정이니만큼 간단하게 필요한 데이터만 넣었다
링크 객체
| 이름 | 타입 | 설명 |
|---|---|---|
| id | UUID() | 기본키 |
| title | String | 사용자 지정 제목 |
| url | String | 메타데이터에서의 url |
| url_type | String | 메타데이터에서 icon / image 중 어떤 이미지로 보여줄지 (값 : icon, image 2가지) |
| category_id | UUID() | 외래키 (카테고리 객체의 기본키) |
카테고리 객체
| 이름 | 타입 | 설명 |
|---|---|---|
| id | UUID() | 기본키 |
| title | String | 카테고리 이름 |
| imoji | String | 카테고리 아이콘 (기본 이모지 활용) |
일단 이정도면 된 것 같다
뭐에 대한 객체인지 궁금하다면, 지난 포스팅을 보면 이해할 수 있을지도?
지난 포스팅 : [iOS] URL 미리보기뷰 커스텀 - LinkPresentation #2
프로젝트를 만들 때 Use Core Data를 이미 체크해두었다
바로 객체를 만들어보자

기본적으로 프로젝트 생성할 때 CoreData를 추가하면 PersistenceController로 예제 코드가 생성되어 있다
그러나, 따로 DataController를 생성하여 코드를 적어봤다
필요한 라이브러리는 다음과 같다
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)")
}
}
}
}
객체에 데이터를 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")
}
}
}
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)
}
}
이 부분에서 한참 헤맸었는데, 기존 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를 통해 모든 뷰에 전해준다
Thread 1: "executeFetchRequest:error: A fetch request must have an entity."
오류가 계속 떠서 이것저것 계속 구조 변경해보면서 뷰를 연결했었는데,
다른 오류들은 처음 persistance 예제를 참고하면 해결되는 것들이었다
이제 마지막에 DataController에 @MainActor 추가해주니까 해결되었다
@MainActor
class DataController: ObservableObject { ... }
대충 폼 형태 만들어서 데이터가 잘 추가되는지만 확인해봤다
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)
})
}
}

텍스트필드에 url과 title(선택)을 입력하면 데이터가 추가되어 아래 리스트뷰에 보여지는 형태이다
잘 동작한다 뿌듯
이제는 더이상 디자인을 미룰 수 없다
CoreData는 1차적으로 연결은 해뒀으니, 뷰를 먼저 그린 후 기능을 추가하는 방향으로 개발해야할 것 같다
참고한 글 (본문 삽입 외)
https://velog.io/@nala/iOS-SwiftUI에서-CoreData-써보기
https://velog.io/@leeesangheee/Core-Data-사용해-CRUD-구현하기