21
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

データクラスを簡単に生成する方法(Swift)

Last updated at Posted at 2022-11-10

はじめに

以下のような構造体があるとします。

SaunaSet.swift
struct SaunaSet {
  var sauna: Sauna
  var coolBath: CoolBath
  var relaxation: Relaxation

  struct Sauna {
    var time: TimeInterval?
  }

  struct CoolBath {
    var time: TimeInterval?
  }

  struct Relaxation {
    var time: TimeInterval?
    var place: String?
    var way: String?
  }
}

本記事では、データ構造のみを持つ構造体を「データクラス」と呼ぶことにします。

このようなデータクラスで、「値がすべて nil 」や「予めデフォルト値がセットされている」、「プレビューやテスト用にいろいろな値がセットされている」など、様々なパターンが欲しいときがあります。

そのようなときにどう実装するのがいいか紹介します。

環境

  • OS:macOS Monterey 12.5.1
  • Xcode:14.1 (14B47b)
  • Swift:5.7.1

様々なデータクラスを生成する

様々なデータクラスを生成する方法を紹介します。

値がすべてnil

先ほどの例で、値がすべて nil のインスタンスが欲しいとします。

普通に生成すると以下のようになります。

let saunaSet = SaunaSet(sauna: .init(time: nil), coolBath: .init(time: nil), relaxation: .init(time: nil, place: nil, way: nil))

1箇所のみで生成するならいいですが、複数箇所で生成すると、毎回この量を書くのは大変です。

そこで CGRect.null に倣い、各データクラスに .null を用意します。

SaunaSet.swift
struct SaunaSet {
+ static var null: Self { .init(sauna: .null, coolBath: .null, relaxation: .null) }
+ 
  var sauna: Sauna
  var coolBath: CoolBath
  var relaxation: Relaxation

  struct Sauna {
+   static var null: Self { .init(time: nil) }
+ 
    var time: TimeInterval?
  }

  struct CoolBath {
+   static var null: Self { .init(time: nil) }
+ 
    var time: TimeInterval?
  }

  struct Relaxation {
+   static var null: Self { .init(time: nil, place: nil, way: nil) }
+ 
    var time: TimeInterval?
    var place: String?
    var way: String?
  }
}

これで SaunaSet 自体も .null だけで、要素がすべて nil のインスタンスを生成できるようになります。

- let saunaSet = SaunaSet(sauna: .init(time: nil), coolBath: .init(time: nil), relaxation: .init(time: nil, place: nil, way: nil))
+ let saunaSet: SaunaSet = .null

CGRect には要素がすべて 0.zero も用意されているので、こちらも参考になります。

.empty.blank のようなプロパティを用意するのもいいと思います。

プレビュー用の値がセットされている

先ほどの例で、プレビュー用に様々な値がセットされたインスタンスが欲しいとします。

基本的には .null と同様ですが、プレビューはデバッグ時にしか使わないため、エクステンションで一番下に区切って #if DEBUG で括るのがオススメです。

SaunaSet.swift
struct SaunaSet {
  // ...
}
+ 
+ #if DEBUG
+ extension SaunaSet {
+   static var preview: Self {
+     .init(
+       sauna: .init(time: 5 * 60),
+       coolBath: .init(time: 30),
+       relaxation: .init(
+         time: 10 * 60,
+         place: "外気浴",
+         way: "インフィニティチェア"
+       )
+     )
+   }
+ }
+ #endif

これでデバッグ時のみ .preview でプレビュー用のインスタンスを取得できます。

SakatsuView.swift
struct SakatsuView: View {
  var saunaSet: SaunaSet
  // ...
}

struct SakatsuView_Previews: PreviewProvider {
  static var previews: some View {
    SakatsuView(saunaSet: .preview) // プレビュー用のインスタンスを取得している
  }
}

デフォルト値がセットされている

先ほどの例で、デフォルト値がセットされているインスタンスが欲しいとします。

.null に倣って .default を実装すると以下のようになります。
デフォルト値は予約語なので、バッククォートで括る必要があります。

SaunaSet.swift
struct SaunaSet {
+ static var `default`: Self { .init(sauna: .default, coolBath: .default, relaxation: .default) }
+ 
  var sauna: Sauna
  var coolBath: CoolBath
  var relaxation: Relaxation

  struct Sauna {
+   static var `default`: Self { .init(time: nil) }
+ 
    var time: TimeInterval?
  }

  struct CoolBath {
+   static var `default`: Self { .init(time: nil) }
+ 
    var time: TimeInterval?
  }

  struct Relaxation {
+   static var `default`: Self { .init(time: nil, place: nil, way: nil) }
+ 
    var time: TimeInterval?
    var place: String?
    var way: String?
  }
}

ただデフォルト値の場合は他と異なり、プロパティやイニシャライザの引数にもデフォルト値をセットできます。

SaunaSet.swift
struct Sauna {
- var time: TimeInterval?
+ var time: TimeInterval? = nil // プロパティにデフォルト値をセットできる
}
SaunaSet.swift
struct Sauna {
  var time: TimeInterval?
+ 
+ init(time: TimeInterval? = nil) { // イニシャライザの引数にデフォルト値をセットできる
+   self.time = time
+ }

.default を用意するか、Swiftの機能を使うか、どちらがいいのでしょうか。

答えはケースバイケースだと思います。

「デフォルト値のインスタンスに名前を付けたい」「明示的にデフォルト値のインスタンスを生成したい」なら .default を用意し、「一部のプロパティのみデフォルト値を使いたい」「プロパティごとにデフォルト値を使うか判断したい」ならSwiftの機能を使う、がよさそうです。

.default は共有するインスタンスを返す慣習があるので違和感がある」という意見もあり、まさにその通りだと思いました。
私はどちらもデフォルト値には変わらないので許容していますが、避けたほうがいいかもしれません。

まとめ

一言でまとめると「データクラス側に生成ロジックを持つと使い回せて便利」です。

おわりに

これでデータクラスを簡単に生成できます。
他にみなさんがやっている方法があれば、コメントなどで教えていただけると嬉しいです :relaxed:

21
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?