現在、CoreDataの練習として作っているSwiftアプリで、長い間ハマっていたところが解決したので備忘録としてまとめておく。

今回のテーマは記事タイトルのとおり、CoreDataで管理するデータを一覧で取得し、Pickerビューから選択させる方法。

上記が対象の画面だが、指導者はInstructorエンティティとして管理されており、Pickerビューを使って指導者名(String型のname属性)を選択させる。

それでは早速コードを紹介する。
※対象のPicker以外のビューは省略している。

import SwiftUI
import CoreData

struct LessonAddView: View {
  @FetchRequest private var instructors: FetchedResults<Instructor>
  @State private var selectedInstructor: Instructor

  init(moc: NSManagedObjectContext) {
    let fetchRequest: NSFetchRequest<Instructor> = Instructor.fetchRequest()
    fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Instructor.createDate, ascending: true)]
    fetchRequest.predicate = NSPredicate(value: true)
    self._instructors = FetchRequest(fetchRequest: fetchRequest)
    do {
      let tmpInstructors = try moc.fetch(fetchRequest)
      if(tmpInstructors.count > 0) {
        self._selectedInstructor = State(initialValue: tmpInstructors.first!)
      } else {
        self._selectedInstructor = State(initialValue: Instructor(context: moc))
        moc.delete(selectedInstructor)
      }
    } catch {
      fatalError("Init Problem")
    }
  }

  var body: some View {
    VStack {
      Form {
        HStack {
          Text("指導者")
          
          if(instructors.count > 0) {
            Picker("", selection: $selectedInstructor) {
              ForEach(instructors, id: \.self) { (instructor: Instructor) in
                Text(instructor.name)
              }
            }
          }
        }
      }
    }
  }
}

Pickerビュー内、ForEachでFetchedResultsの結果をバインドさせる場合、構造体のイニシャライザ(8行目以降)で状態変数を初期化すると良い。

プレビュー用のコードも紹介しておこう。

struct LessonAddView_Previews: PreviewProvider {
  static var previews: some View {
    let context = (UIApplication.shared.delegate as! AppDelegate)
      .persistentContainer.viewContext

    return LessonAddView(moc: context)
      .environment(\.managedObjectContext, context)
      .environment(\.locale, Locale(identifier: "ja_JP"))
  }
}

これでプレビューを実行すると、CoreDataに登録されたデータ一覧をPickerビューに渡すことができる。