【Swift】モデルクラスの ID に UUID() を使ってたらヤバい - PersistentIdentifier

すいません、別にヤバくはないです。

みなさんはどう書いてますか。

私はこんな感じで書いてました。


final class Card {
  var id: UUID
  var front: String
  var back: String

  init(front: String, back: String) {
    self.id = UUID()
    self.front = front
    self.back = back
  }
}

これを、Apple 公式サンプルではこんなかんじで書いてました。


final class Card {
  var front: String
  var back: String

  init(front: String, back: String) {
    self.front = front
    self.back = back
  }
}

extension Card: Identifiable { }

ID (id) がない!

Playground で見てみます。


final class Card {
  var front: String
  var back: String

  init(front: String, back: String) {
    self.front = front
    self.back = back
  }
}

extension Card: Identifiable { }

let card = Card(front: "前", back: "後")

print(card.id)
print(card.front)
print(card.back)

// ObjectIdentifier(0x0000600000c014d0)
// 前
// 後

id が取れます。

Identifiable を継承しても同様です。

Apple のサンプルコードは少し省略して書きましたが、実際は以下のようなコードです。


final class Card: ObservableObject {
  @Published var front: String
  @Published var back: String
  var creationDate: Date

  init(front: String, back: String, creationDate: Date = .now) {
    self.front = front
    self.back = back
    self.creationDate = creationDate
  }
}

extension Card: Identifiable { }


struct CardCarousel: View {
  @State private var selectedCardID: Card.ID?  // *
  @FocusState private var focusCardID: Card.ID?
  private let initialCardID: Card.ID

  let editing: Bool
  var cards: [Card]

  init(editing: Bool, cards: [Card], selectedCard: Card) {
    self.editing = editing
    self.cards = cards
    initialCardID = selectedCard.id
  }

  var body: some View {
    VStack {
      ScrollView(.horizontal) {
        LazyHStack(spacing: 0) {
         ForEach(cards) { card in // *
           Group {
             if editing {
               CardEditorView(card: card)
             } else {
               FlashCardView(card: card)
                 .id(card.id)

ObjectIdentifier(0x0000600000c014d0) の型は、

Card.ID

です。

また、List 内の ForEach() の引数は一つです。

便利ですよね!

 

🆔 SwiftData の場合

同じく Apple サンプルコードです。

SwiftData では更に便利になっています。

こちらにもモデルクラスに ID はありません。

Identifiable もありません。


@Model
final class Card {
  var front: String
  var back: String
  var creationDate: Date

  init(front: String, back: String, creationDate: Date = .now) {
    self.front = front
    self.back = back
    self.creationDate = creationDate
  }
}


struct CardCarousel: View {
  @State private var selectedCardID: PersistentIdentifier? // *
  @FocusState private var focusCardID: PersistentIdentifier?
  private let initialCardID: PersistentIdentifier

  let editing: Bool
  var cards: [Card]

  init(editing: Bool, cards: [Card], selectedCard: Card) {
    self.editing = editing
    self.cards = cards
    initialCardID = selectedCard.id
  }

  var body: some View {
    VStack {
      ScrollView(.horizontal) {
        LazyHStack(spacing: 0) {
          ForEach(cards) { card in // *
            Group {
              if editing {
                CardEditorView(card: card)
              } else {
                FlashCardView(card: card)
                  .id(card.id)

ID の型は、PersistentIdentifier です。

書き方は違いますが、同様にモデルクラスにIDなしで使えます。

 

🆔 まとめ

今回勉強した内容で、以前の Todo アプリを書き換えます。

👉 SwiftUI + SwiftData で ToDo リスト を作ってみる hatena-bookmark

少しだけスッキリしました。