ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Core Data with SwiftUI Tutorial: Getting Started
    Raywenderlich/Articles 2020. 12. 9. 15:28

    www.raywenderlich.com/9335365-core-data-with-swiftui-tutorial-getting-started

     

    Core Data with SwiftUI Tutorial: Getting Started

    In this Core Data with SwiftUI tutorial, you’ll learn to persist data in an app using @State, @Environment and @FetchRequest property wrappers.

    www.raywenderlich.com

    Version

    Swift 5, iOS 13, Xcode 11

     

    다음 번에 앱(app)을 열 때, Notes에 적어둔(jotting down) 중요(important) 내용이 사라진다는 것을 상상하면 끔찍하다. 다행히도(fortunately) iOS의 지속성(persistence)은 훌륭하다. Core Data 덕분에 모든 메모(notes), 사진(photos), 기타 데이터(data)는 안전하다.

    앱(app) 실행(launches) 전반에 데이터를 저장(store)해야 할 때 사용할 수 있는 다양한 기술(technologies)이 있다. Core Data는 iOS에서 사용할 수 있는 해결책(solution)이다. 인상적인(impressive) 성능(performance)과 다양한(broad) 기능(features)을 갖춘 Apple의 Core Data 프레임 워크(framework)는 앱(app)의 전체(entire) 모델 계층(model layer)을 관리(manages)하고, 기기(device)의 저장 디스크(storage disk)에 대한 지속성(persistence)을 처리(handles)한다.

    이 Core Data with SwiftUI 튜토리얼(tutorial)에서는 앱(app)을 리팩토링(refactor)하고 지속성(persistence)을 추가하여 앱(app)이 다시 시작(restarts)될 때 데이터가 손실되는 악몽(nightmare)을 방지(prevent)한다. 그 과정에서 다음을 배우게 된다:

    • 프로젝트(project)에서 Core Data를 설정(set up)한다.
    • SwiftUI의 데이터 흐름(data flow)을 사용하여, Core Data 프레임 워크(framework)에서 필요한 항목에 접근(access)한다.
    • Core Data를 사용하여 새로운 모델(model) 객체(objects)를 정의(define)하고 생성한다.
    • Fetch Requests을 사용하여, 디스크(disk)에서 객체(objects)를 가져(retrieve)온다.

    이제 Core Data의 기능(capabilities)과 작동 방식(how it works)에 대해 자세히 알아본다.

     

    Getting Started

    시작하려면 이 튜토리얼(tutorial)의 상단(top) 또는 하단(bottom)에 있는 Download Materials 버튼(button)을 사용하여 프로젝트(project) 자료(materials)를 다운로드(download) 한다. Xcode에서 시작(starter) 프로젝트(project)를 연 후에, 빌드(build)하고 실행(run)한다.

     

    FaveFlicks 앱은 개인(personal)적으로 선호하는(favorite) 영화를 기록(tally)한다. 목록(list)에 영화를 추가(add)하거나 삭제(delete)할 수 있는 간단한 앱이다. 그러나(however), 한 가지 분명한(glaring) 문제(problem)가 있다.

    앱(app)은 데이터를 유지(persist)하지 않는다. 즉, 목록(list)에 일부 영화를 추가한 다음 앱을 재시작(restart)하면 신중하게(carefully) 추가(added)한 영화가 사라진다.

     

    Testing FaveFlick’s Persistence

    목록(list)에서 영화를 제거하려면, 왼쪽으로 밀어(swipe) Delete를 탭(tap)한다.

     

    그런 다음 오른쪽 상단(top-right)의 Plus 버튼(button)을 탭(tap)하여, 즐겨찾기(favorites) 중 하나를 추가(add)한다.

     

    영화 추가(Add Movie) 화면(screen)이 표시된다.

     

    Movie 객체(object)는 메모리에만 존재(exists)한다. 디스크(disk)에 저장(stored)되지 않으므로, 앱(app)을 닫으면 변경 사항(changes)이 제거(removes)되고 영화 즐겨찾기(favorite) 목록(list)으로 돌아간다.

    Note: "add movie" 화면(screen)을 두 번째로 열려고 하면 아무 일도 일어나지 않는다(nothing happens). 이는 SwiftUI의 알려진 Apple 버그이다. 해결 방법(workaround)으로 어떤 방식으로든 영화를 추가하여, UI를 업데이트(update)해야 한다. 목록(list)을 아래로 당겨서(pull down) UI를 업데이트(update)한 다음, 더 많은 영화를 추가(add)할 수 있다.

    앱(app)을 강제 종료(force close)하여 지속성(persistence)을 확인(test)한다. 앱(app)이 전면(foreground)에 있는 상태에서 빠른 앱 전환 화면(fast app switcher)으로 들어간다(enter). 이렇게 하려면 화면(screen)의 하단(bottom)에서 부드럽게(gently) 위로 드래그(drag up)한다. 홈 버튼이 있는 기기(device)에서 빠른 앱 전환(fast app switcher)을 사용하려면 홈(Home) 버튼(button)을 두 번 탭(double-tap)한다.

     

    이제 FaveFlicks를 선택(select)하고 위로 스와이프(swipe up)하여 앱(app)을 닫는다(close). 그리고 홈 화면(home screen)에서 FaveFlicks를 탭(tap)하여 다시 연다.

    변경 사항(changes)이 사라지고(gone), 기본(default) 영화가 다시 표시되는 것을 확인할 수 있다.

     

    문제를 해결(fix)해야할 때이다. 먼저 Core Data를 설정한다.

     

    Setting Up Core Data

    지속성(persistence) 설정(setting up)을 시작하기 전에, Core Data stack 이라고도 하는 Core Data의 운송(moving) 부분에 대해 알아야 한다. Core Data stack에는 다음이 포함(includes)된다:

    • managed object model엔티티(entities)라고 하는 모델(model) 객체(objects)와 다른 엔티티(entities)와의 관계(relationships)를 정의(defines)한다. 이를 데이터베이스(database) 스키마(schema)라고 생각하면 된다. FaveFlicks에서는 FaveFlicks.xcdatamodeld 내(inside)의 managed object model 일부로 Movie 엔티티(entity)를 정의(define)한다. NSManagedObjectModel 클래스(class)를 사용하여, 코드에서 managed object model에 접근(access)한다.
    • NSPersistentStoreCoordinator는 실제 데이터베이스(database)를 관리(manages)한다.
    • NSManagedObjectContext는 엔터티(entities)를 생성(create), 편집(edit), 삭제(delete) 또는 가져올(retrieve) 수 있는 메모리 스크래치 패드(in-memory scratchpad)이다. 일반적으로(usually) Core Data와 상호 작용(interacting)할 때 managed object context로 작업한다.
    Scratchpad memory : en.wikipedia.org/wiki/Scratchpad_memory

    이제 시작해볼 시간이다(it’s time to get started).

     

    Adding the Core Data stack

    전체(entire) Core Data stack을 설정(set up)하는 것은 어려워 보일 수 있지만, NSPersistentContainer 덕분에 쉽게 구성할 수 있다. 이를 사용해 모든 것을 만들 수 있다. SceneDelegate.swift를 열고 import SwiftUI 다음에 아래 코드를 추가한다:

    import CoreData

    Core Data는 자체(own) 프레임 워크(framework)이므로, 이를 사용하려면 반드시 가져와야(import) 한다.

    이제 SceneDelegate의 끝에 다음을 추가한다:

    lazy var persistentContainer: NSPersistentContainer = {
      //lazy 속성으로 persistentContainer를 추가한다.
      //해당 속성을 처음 참조할 때, NSPersistentContainer가 생성된다.
      let container = NSPersistentContainer(name: "FaveFlicks")
      //FaveFlicks 이름의 container를 생성한다.
      //이 프로젝트에는 이미 FaveFlicks.xcdatamodeld가 있으며, 이 파일은 Core Data의 model schema를 설계한다.
      //해당 파일의 이름은 container의 이름과 일치해야 한다.
      
      container.loadPersistentStores { _, error in
        //Core Data stack을 설정하는 persistent store를 로드한다.
        
        if let error = error as NSError? {
          // You should add your own error handling code here.
          fatalError("Unresolved error \(error), \(error.userInfo)")
          //error가 발생하면 이를 기록하고 앱을 종료시킨다.
          //실제 앱에서는 앱의 state가 비정상이며 재설치가 필요하다는 dialog를 표시하여 처리한다.
          //이 error는 발생할 확률이 매우 낮고 개발자의 실수로 인한 것이므로, App Store에 출시하기 전에 처리할 수 있어야 한다.
        }
      }
      return container
    }()

    이것이 Core Data stack을 설정(set up)하는 데 필요한 전부이다. 정말 좋다(Quite a journey).

    Core Data는 자동으로(automatically) 처리(handle)되지 않기 때문에, 데이터를 디스크(disk)에 저장(saving)하는 방법도 필요하다. SceneDelegate.swift에서 클래스(class)의 끝에 다음 메서드(method)를 추가한다:

    func saveContext() {
      let context = persistentContainer.viewContext
      //persistent container의 viewContext를 얻는다.
      //이는 main thread에서만 사용되도록 지정된 특별한 managed object context이다.
      //저장되지 않은 data를 저장하는데 이를 사용한다.
      if context.hasChanges {
        //저장할 변경 사항이 있는 경우에만 저장한다.
        do {
          try context.save()
          //context를 저장한다. 이 호출은 error를 발생시킬 수 있으므로 try/catch로 감싼다.
        } catch {
          // The context couldn't be saved.
          // You should add your own error handling here.
          let nserror = error as NSError
          fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
          //error가 발생하면, 이를 기록하고 앱이 종료된다.
          //이전 method에서와 마찬가지로 여기의 모든 error는 개발 중에서만 발생해야 한다.
          //하지만 만약의 경우를 대비해 app에서 적절하게 처리한다.
        }
      }
    }

    이제 Core Data stack을 설정(set up)하고, 변경 사항(changes)을 저장(save)하는 메서드(method)가 있으므로, 이를 앱(app)의 나머지 부분에 연결(wire)할 차례이다.

    이제 scene(_:willConnectTo:options:)에서 let contentView = MovieList()를 다음으로 바꾼다(replace):

    let context = persistentContainer.viewContext
    let contentView = MovieList().environment(\.managedObjectContext, context)

    이전에 사용한 것과 동일한 viewContext를 가져와(grabs), MovieList SwiftUI 뷰(view)의 환경 변수(environment variable)로 설정(sets)한다. 뷰(view)는 나중에 이를 사용하여 Core Data 저장소(store)에 영화를 추가(add)하고 제거(remove)한다.

    이제 SceneDelegate 끝에 다음 메서드(method)를 추가한다:

    func sceneDidEnterBackground(_ scene: UIScene) {
      saveContext()
    }

    이렇게 하면 앱(app)이 백그라운드(background)로 전환될 때, 이전에 추가한 save 메서드(method)를 호출(call)한다. 해당 상황은 데이터를 디스크(disk)에 저장(save)하기 좋은 상황이다. 더 자주(frequently) 저장하는 방법에 대해서는 이후에 알아볼 것이다.

    빌드(build)하고 실행(run)하여, 앱(app)이 제대로 작동하는지 확인한다. 아직 기능적(functional)인 변경 사항(changes)은 없다.

     

    Creating the Data Model

    이제 Core Data stack을 벗어나, 드디어 앱(app)의 주요 부분(main part)을 작업할 차례이다. Xcode에서 FaveFlicks.xcdatamodel을 연다. 지금은 비어(empty) 있지만, 아래에서 Movie 엔티티(entity)를 선언(declare)할 것이다. 여기에서 데이터 모델(data model)의 스키마(schema)를 정의(define)한다. 생성할 수 있는 객체(objects) 유형(types)인 관련(relevant) 엔티티(entities)를 추가하고, 엔티티(entities)가 연결(connected)되는 방식을 나타내는(indicate) 관계(relationships)를 정의(define)한다.

    엔티티 추가(Add Entity)를 클릭(click)한다.

    Xcode는 데이터 모델(data model)에서, 기본적(default)으로 Entity라는 새 엔티티(entity)를 생성한다. 이름을 두 번 클릭(Double-click)하여 Movie로 변경(change)한다.

     

    다음으로 Attributes 아래의 + 아이콘(icon)을 클릭(click)하여 새 속성(attribute)을 추가한다. 이름을 title로 지정하고, 유형(type)을 String로 설정한다.

     

    마지막으로(finally), 두 개의 속성(attributes)을 추가한다. 하나는 String 유형(type)의 genre이고, 다른 하나는 Date 유형(type)의 releaseDate이다. 완료(done)되면, Movie 엔티티(entity)의 속성(attributes)은 다음과 같다:

     

    Relationships and Fetched Properties

    FaveFlicks에는 Movie라는 하나의 엔터티(entity)만 있지만, 더 큰 데이터 모델(data models)을 사용하는 앱(apps)에서는 관계(relationships)를 설정(run into)하고 속성(properties)을 가져올(fetched) 수 있다. 관계(relationship)는 모든 데이터베이스(database)의 관계(relationship)와 동일하게, 두 엔티티(entities) 간의 관계(relationships)를 정의(define)할 수 있다.

    그러나 Fetched properties는 고급(advanced) Core Data 주제(topic)이다. weak 단방향(one-way) 관계(relationships)처럼 작동하는 computed properties로 생각할 수 있다. 예를 들어(for instance) FaveFlicks에 Cinema 엔터티(entity)가 있는 경우, 현재 영화관에서 상영 중인 영화를 가져 오는 currentShowingMovies라는 fetched property가 있을 수 있다.

    Note: 이 두 가지 모두에 대한 자세한 정보(information)와 더 많은 자료를 원한다면 Core Data by Tutorials를 확인(check out)해 본다.

     

    Removing the Old Movie Struct

    Movie.swift를 연다. 이 튜토리얼(tutorial)을 시작할 때, Movie 구조체(struct)는 모델(model) 객체(object)였다. Core Data는 자체 Movie 클래스(class)를 생성하므로 Movie.swift를 제거(remove)해야 한다. 프로젝트 탐색기에서 Movie.swift를 마우스 오른쪽 버튼으로 클릭(right-clicking)하고, Delete를 선택하여 삭제(delete)한다. 표시되는 대화 상자(dialog)에서 Move to Trash을 클릭(click)한다.

    앱(app)을 빌드(build)한다. 방금 Movie를 삭제(removed)했기 때문에, 수정(fixing)해야 하는 몇 가지 오류(a couple of errors)가 표시된다.

     

    Note: 이 섹션(section)에서 이전 Movie 구조체(struct)에 대한 코드를 정확(precise)하게 삭제(delete)해야 하므로 자세히 확인한다.

    먼저 MovieList.swift를 연다. 간단하게 movies 배열(array)에 저장(stored)된 영화 목록(list)을 확인할 수 있다. MovieList의 상단(top)에서 movies 배열(array)을 선언(declaring)하는 행(line)을, 아래와 같이 빈(empty) 배열(array)로 변경(change)한다:

    @State var movies: [Movie] = []

    @State 속성 래퍼(property wrapper)는 SwiftUI 데이터 흐름(data flow)의 중요한 부분(vital piece)이다. 이 로컬 속성(local property)을 선언(declares)하는 클래스(class)가 이를 소유(owns)한다. movies의 값(value)이 변경(changes)되면, 이를 소유한(owns) 뷰(view)는 UI 업데이트(update)를 실행(trigger)한다.

    이제 makeMovieDefaults()는 더 이상 사용되지 않으므로, 삭제(delete)한다.

    addMovie(title:genre:releaseDate:)에서 영화가 생성되고, movies 배열(array)에 추가(added)된다. 해당 내용(contents)을 제거(remove)하고 빈(blank) 메서드(method)로 남겨둔다. 이후 섹션(section)에서 이를 사용하여, Movie 엔티티(entity)의 새 인스턴스(instances)를 만든다.

    마지막으로 deleteMovie(at:)의 내용(contents)을 제거(remove)한다. 나중에 이를 Core Data의 엔티티(entities)를 삭제(deletes)하는 코드로 대체(replace)한다.

     

    Using the New Movie Entity

    이제 데이터 모델(data model)에서 Movie 엔티티(entity)를 만들었으므로, Xcode는 대신(instead) 사용할 자체 Movie 클래스(class)를 자동으로 생성(auto-generate)한다. 데이터 모델(data model)의 모든 엔티티(entities)는 NSManagedObject의 하위 클래스(subclasses)이다. Core Data는 주로 Managed Object Context를 사용하여 Core Data의 수명주기(lifecycle)와 지속성(persistence)을 처리(handles)하므로, 엔티티는 managed object이다.

    이전(old) Movie 구조체(struct)는 옵셔널(optional) 속성(properties)을 사용하지 않았다. 그러나 모든 NSManagedObject 하위 클래스(subclasses)는 해당 속성(attributes)에 대해 옵셔널(optional) 속성(properties)을 사용한다. 즉, Movie를 사용하는 파일(files)을 일부 변경(changes)해야 한다.

    Note: 모든 속성(properties)이 옵셔널(optional)인 경우의 예외(exception) 사항은 스칼라(scalar) 유형(types)이다. 예를 들어(for example), 엔터티(entity)에 Float 속성(property)이 있고 Use Scalar Type이 활성화(enabled)된 경우, 생성(generated)된 속성(property)은 비 옵셔널(non-optional) Float 유형(type)이 된다.

     

    Using an Entity’s Attributes in a View

    이제 뷰(view)에서 엔티티(entity)의 속성(attributes)을 사용하는 방법을 배운다. MovieRow.swift를 열고, body 속성(property)을 다음으로 바꾼다(replace):

    var body: some View {
      VStack(alignment: .leading) {
        // 1
        movie.title.map(Text.init)
          .font(.title)
        HStack {
          // 2
          movie.genre.map(Text.init)
            .font(.caption)
          Spacer()
          // 3
          movie.releaseDate.map { Text(Self.releaseFormatter.string(from: $0)) }
            .font(.caption)
        }
      }
    }

    뷰(view)의 구조(structure)는 정확히 동일하지만, 모든 movie의 속성(attributes)이 View에 매핑(mapped)되어 있음을 알 수 있다.

    Core Data 엔터티(entity)의 모든 속성(attributes)은 옵셔널(optional)이다. 즉, title 속성(optional)은 String? 유형(type)이고 referenceDateDate? 유형(type)이다. 따라서 이제 옵셔널(optional)의 값(value)을 얻을 방법이 필요하다.

    MovieRowbody 속성(property)과 같은 ViewBuilder에서는, 내부(inside)에 if let과 같은 제어 흐름(control flow) 구문(statements)을 추가할 수 없다. 각 행(line)은 View 또는 nil이어야 한다.

    위의 1, 2, 3으로 표시된 행(line)은 속성(attributes)이 nil이 아닌 경우(non-nil), Text 뷰(view)가 된다. 그렇지 않으면(otherwise) nil이 된다. 이는 SwiftUI 코드에서 옵셔널(optionals)을 처리(deal with)하는 편리한(handy) 방법이다.

    마지막으로 빌드(build)하고 실행(run)한다. 이전(old) Movie 구조체(struct)를 제거(removed)하고 Core Data 엔터티(entity)로 대체(replaced it with)했다. 그에 대한 보상(reward)으로, 이제는 세련된(classy) 영화 목록(list)이 아닌 빈(empty) 뷰(view)를 갖게 된다.

     

    영화를 생성해도, 아무 일이 일어나지 않는다(nothing happens). 다음으로 이 문제를 해결한다.

     

    Using Environment to Access Managed Object Context

    다음으로 managed object context에서 객체(objects)에 접근(access)하는 방법을 배운다. MovieList.swift로 돌아가, movies 선언(declaration) 아래에 다음 행(line)을 추가한다:

    @Environment(\.managedObjectContext) var managedObjectContext

    이전에 MovieList에서 managedObjectContext 환경 변수(environment variable)를 설정했다. 이제 그것에 접근(access)할 수 있게 선언(declaring)한다.

    @Environment는 전역(global) 속성(properties)에 접근(access)할 수있는 SwiftUI 데이터 흐름(data flow)의 또 다른 중요한 부분(important piece)이다. 환경 객체(environment object)를 뷰(view)에 전달(pass)하려면, 객체(object)를 생성할 때 전달(pass)한다.

    이제 MovieList.swift에 다음 메서드(method)를 추가한다:

    func saveContext() {
      do {
        try managedObjectContext.save()
      } catch {
        print("Error saving managed object context: \(error)")
      }
    }

    엔터티(entities)를 생성(create), 업데이트(update) 또는 삭제(delete)할 때, 메모리 스크래치 패드(in-memory scratchpad)인 managed object context에서 이를 수행한다. 실제로 변경 사항(changes)을 디스크(disk)에 기록(write)하려면, 컨텍스트(context)를 저장(save)해야 한다. 이 메서드(method)는 새로운 객체(objects) 또는 업데이트(updated)된 객체(objects)를 영구 저장소(persistent store)에 저장(saves)한다.

    다음으로, addMovie(title:genre:releaseDate:)를 찾는다. 이전 Movie를 제거하고 메서드(method)는 여전히 비어 있으므로, 아래 메서드(method)로 대체(replace)하여 새 Movie 엔터티(entities)를 생성한다:

    func addMovie(title: String, genre: String, releaseDate: Date) {
      let newMovie = Movie(context: managedObjectContext)
      //managed object context에서 새로운 Movie를 생성한다.
      
      newMovie.title = title
      newMovie.genre = genre
      newMovie.releaseDate = releaseDate
      //addMovie(title:genre:releaseDate:)로 전달된 매개변수를 Movie의 모든 속성에 설정한다.
      
      saveContext()
      //managed object context를 저장한다.
    }

    빌드(build)하고 실행(run)한 후, 새 영화를 생성한다. 여전히 빈(blank) 목록(list)이 표시된다.

     

    영화를 만들었지만, 목록(list)에 표시(display)하기 위해 가져오지(retrieving) 않았기 때문이다. 다음 섹션(section)에서 이 문제를 해결(fix)하면, 마침내 앱(app)에서 영화를 다시 보게 될 것이다.

     

    Fetching Objects

    이제 생성한 영화를 표시(display)하는 방법을 배운다. FetchRequest를 사용하여 영구 저장소(persistent store)에서 이를 가져와야(fetch) 한다.

    MovieList의 상단(top)에서 movies 배열(array)을 선언(declaring)하는 행(line)을 제거(remove)한다. 그리고 이를 FetchRequest로 바꾼다(replace):

    @FetchRequest(
      //@FetchRequest 속성 래퍼를 사용하여 속성을 선언하면, SwiftUI 뷰에서 결과를 직접 사용할 수 있다.
      entity: Movie.entity(),
      //속성 래퍼 내에서 가져올 Core Data 엔티티를 지정한다.
      //여기서는 Movie 엔티티의 instances를 fetch한다.
      sortDescriptors: [
        //결과의 순서를 결정하기 위해, sort descriptors 배열을 추가한다.
        //예를 들어, genre별로 영화를 정렬한 다음 같은 genre의 영화에 대해서는 title별로 정렬할 수 있다.
        NSSortDescriptor(keyPath: \Movie.title, ascending: true)
        //여기서는 단순하게 title로 정렬한다.
      ]
    ) var movies: FetchedResults<Movie>
    //마지막으로, property wrapper 다음에 FetchedResults 유형의 movies 속성을 선언한다.

    Core Data에서 엔티티(entities)를 가져와야(retrieve) 하는 경우, FetchRequest를 생성한다.

     

    Predicates

    위의 구현은 Core Data에 저장(stored)된 모든 Movie를 가져온다(fetch). 그러나 객체(objects)를 필터링(filter)해야 하거나 특정(specific) 엔티티(entity) 하나만 가져와야(retrieve) 하는 경우가 있을 수 있다. 또한 특정(certain) 연도(year) 또는 특정 (certain) 장르(genre)의 Movie만 가져오는(fetching) 것과 같이, 결과를 제한(limit)하는 predicate를 사용하여 fetched request를 구성(configure)할 수도 있다. 그렇게 하려면 다음과 같이 @FetchRequest 속성 래퍼(property wrapper) 끝에 predicate 매개 변수(parameter)를 추가한다:

    predicate: NSPredicate(format: "genre contains 'Action'")

    지금은 fetch request이 모든 Movie를 가져와야 하므로, 추가할 필요가 없다. 하지만 결과를 확인해 보고 싶다면 꼭 해보는 것이 좋다(if you want to play around with this then by all means do).

     

    Testing the Results

    빌드(build)하고 실행(run)한다. 영화의 목록(list)이 표시된다.

     

    처음 시작한 곳으로 돌아왔다. 영화가 디스크(disk)에 저장(storing)되는지 확인(test)하려면 몇 개를 추가한 다음, Xcode에서 중지(stop)를 눌러 앱을 종료(kill the app )한다. 그런 다음 다시 빌드(build)하고 실행(run)한다. 모든 영화는 그대로 유지된다.

     

    Deleting Objects

    다음으로 객체(objects)를 삭제(delete)하는 방법을 배운다. 왼쪽으로 스와이프(swipe left)하고 영화를 삭제(delete)하려고 하면 아무 일도 일어나지 않는다(nothing happens). 이 문제를 해결(fix)하려면 deleteMovie(at:)를 다음으로 바꾼다(replace):

    func deleteMovie(at offsets: IndexSet) {
      offsets.forEach { index in
        //SwiftUI의 List는 목록에서 객체를 삭제하기 위해 swipe할 때, 삭제할 IndexSet를 제공한다.
        //forEach를 사용하여 IndexSet을 반복한다.
        let movie = self.movies[index]
        //현재 index에 대한 movie를 가져온다.
        self.managedObjectContext.delete(movie)
        //managed object context에서 movie를 삭제한다.
      }
      saveContext()
      //context를 저장하여, 변경사항을 디스크에 유지한다.
    }

    빌드(build)하고 실행(run)한 다음, 영화를 삭제(delete)한다.

     

    앱의 모든 기능(functionality)을 복원(restored)했으며, 게다가 Core Data 덕분에(thanks to) 아침에도 앱(app)이 그대로 있을 것이다.

     

    Where to Go From Here?

    Core Data를 사용하여 SwiftUI 프로젝트(project)에 지속성(persistence)을 도입(introduce)하고, 엔티티(entities)를 디스크(disk)에 저장(store)했다. 이 튜토리얼(tutorial)의 상단(top) 또는 하단(bottom)에 있는 Download Materials 버튼(button)을 사용하여, 완성(completed)된 프로젝트(project)를 다운로드(download)할 수 있다.

    Core Data 및 SwiftUI에 대한 더 많은 실제(hands-on) 경험(experience)을 원한다면, 다음 튜토리얼(tutorials)들을 확인해 본다:

    Core Data는 방대한(huge) 주제(topic)이며, 아직 배워야 할 것이 많다. UIKit을 사용한 Core Data에 대해 자세히 알아 보려면, Core Data by Tutorials를 살펴본다.

    이 튜토리얼(tutorial)에서 데이터 흐름(data flow)에 대해 꽤(a fair bit of) 살펴 보았지만, 자세히 알아보려면 WWDC 2019의 Data Flow Through SwiftUI 비디오를 살펴볼 수 있다.

    데이터 흐름(data flow)뿐만 아니라 SwiftUI 마스터(master)가 되기 위한 모든 것이 필요하다면, SwiftUI by Tutorials에서 필요한 부분을 확인해 본다.

    SwiftUI 덕분에(thanks to) Core Data 설정(set up)이 그 어느 때보다 쉬워졌다(easier than ever before). 이 Core Data with SwiftUI Tutorial 튜토리얼(tutorial)이 즐거웠기를 바란다. 공유할(share) 질문(questions)이나 통찰(insights)이 있다면, 아래의 포럼(forum)에 참여(join)할 수 있다.

Designed by Tistory.