Slide 1

Slide 1 text

ウーニャ、しってる。 みんな雰囲気で SwiftUI をつかってる。 2022.09.10(Sat) 17:15 - 17:55
 iOSDC Japan 2022 day 0 Track A @the_uhooi

Slide 2

Slide 2 text

iOS app developer @uhooi @uhooi @the_uhooi

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

今日は SwiftUI における View の
 「分割」と「命名」について
 みんなで議論したいです #iosdc #ウーニャ #a 感想をつぶやいてね!

Slide 8

Slide 8 text

好きな SwiftUI の View について ツイートしてみましょう! ※なければ「┌|▼▼|┘」とつぶやいてください #iosdc #a #ウーニャ

Slide 9

Slide 9 text

・分割と命名を工夫する主な目的は、可読性の向上 ・View 層のみを対象とする  ・MVC や MVVM でいう「V」 ・本発表は私個人の意見 ・(オンライン向け)スクショ OK!  ・SNS やブログに使ってね 注意

Slide 10

Slide 10 text

01 View の「分割」

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

struct var var var some init : View { iconName: name: body: View { ( : ) { (iconName) . () . () . ( : , : ) (name) . (.title) . ( : . , : .leading) } . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } } MonsterListCellView String String HStack spacing 32 Image resizable scaledToFit frame width 68 height 68 Text font frame maxWidth infinity alignment padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1

Slide 13

Slide 13 text

現状: body 内が1つの大きな View になっている ↓ ゴール: body 内の View を分割して読みやすくする

Slide 14

Slide 14 text

struct var var var some init : View { iconName: name: body: View { ( : ) { (iconName) . () . () . ( : , : ) (name) . (.title) . ( : . , : .leading) } . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } } MonsterListCellView String String HStack spacing 32 Image resizable scaledToFit frame width 68 height 68 Text font frame maxWidth infinity alignment padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 Image + Text = Label .elevate()

Slide 15

Slide 15 text

Label

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Label を使うと画像とテキストに 関連性を持たせられる!

Slide 18

Slide 18 text

struct MonsterListCellView: View { var iconName: String var name: String var body: some View { } .padding(16) .background( Color(.systemBackground) .cornerRadius(3) .shadow( color: .init(white: 0, opacity: 0.24), radius: 1, x: 0, y: 1 ) - HStack(spacing: 32) { - Image(iconName) - .resizable() - .scaledToFit() - .frame(width: 68, height: 68) - Text(name) - .font(.title) - .frame(maxWidth: .infinity, alignment: .leading) + Label { + Text(name) + } icon: { + Image(iconName) + .resizable()

Slide 19

Slide 19 text

struct MonsterListCellView: View { var iconName: String var name: String var body: some View { } .padding(16) .background( Color(.systemBackground) .cornerRadius(3) .shadow( color: .init(white: 0, opacity: 0.24), radius: 1, x: 0, y: 1 ) - HStack(spacing: 32) { - Image(iconName) - .resizable() - .scaledToFit() - .frame(width: 68, height: 68) - Text(name) - .font(.title) - .frame(maxWidth: .infinity, alignment: .leading) + Label { + Text(name) + } icon: { + Image(iconName) + .resizable()

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

struct func -> some extension where static var init : LabelStyle { ( : Configuration) View { ( : ) { configuration.icon . () . ( : , : ) configuration.title . (.title) . ( : . , : .leading) } } } Self == MonsterListCellLabelStyle { monsterListCell: MonsterListCellLabelStyle { . () } } MonsterListCellLabelStyle makeBody configuration LabelStyle HStack spacing 32 scaledToFit frame width 68 height 68 font frame maxWidth infinity alignment MonsterListCellLabelStyle

Slide 22

Slide 22 text

struct func -> some extension where static var init : LabelStyle { ( : Configuration) View { ( : ) { configuration.icon . () . ( : , : ) configuration.title . (.title) . ( : . , : .leading) } } } Self == MonsterListCellLabelStyle { monsterListCell: MonsterListCellLabelStyle { . () } } MonsterListCellLabelStyle makeBody configuration LabelStyle HStack spacing 32 scaledToFit frame width 68 height 68 font frame maxWidth infinity alignment makeBody() 内に
 表示する View を書く MonsterListCellLabelStyle

Slide 23

Slide 23 text

struct func -> some extension where static var init : LabelStyle { ( : Configuration) View { ( : ) { configuration.icon . () . ( : , : ) configuration.title . (.title) . ( : . , : .leading) } } } Self == MonsterListCellLabelStyle { monsterListCell: MonsterListCellLabelStyle { . () } } MonsterListCellLabelStyle makeBody configuration LabelStyle HStack spacing 32 scaledToFit frame width 68 height 68 font frame maxWidth infinity alignment .labelStyle(MonsterListCellLabelStyle()) を
 .labelStyle(.monsterListCell) と書くための
 おまじない MonsterListCellLabelStyle

Slide 24

Slide 24 text

struct MonsterListCellView: View { var iconName: String var name: String var body: some View { Label { Text(name) } icon: { Image(iconName) .resizable() } .padding(16) .background( Color(.systemBackground) .cornerRadius(3) .shadow( color: .init(white: 0, opacity: 0.24), radius: 1, x: 0, y: 1 ) ) } } + .labelStyle(.monsterListCell)

Slide 25

Slide 25 text

struct MonsterListCellView: View { var iconName: String var name: String var body: some View { Label { Text(name) } icon: { Image(iconName) .resizable() } .padding(16) .background( Color(.systemBackground) .cornerRadius(3) .shadow( color: .init(white: 0, opacity: 0.24), radius: 1, x: 0, y: 1 ) ) } } + .labelStyle(.monsterListCell)

Slide 26

Slide 26 text

・Label を使うと、画像とテキストに関連性を
  持たせられる ・Label のカスタマイズには LabelStyle を使う Label のまとめ

Slide 27

Slide 27 text

View の分割方法

Slide 28

Slide 28 text

struct var var var some : init : View { iconName: name: body: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } } MonsterListCellView String String Text Image resizable labelStyle padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1

Slide 29

Slide 29 text

struct var var var some : init : View { iconName: name: body: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } } MonsterListCellView String String Text Image resizable labelStyle padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 body 内の
 単一の View が
 少しボリューミー

Slide 30

Slide 30 text

struct var var var some : init : View { iconName: name: body: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } } MonsterListCellView String String Text Image resizable labelStyle padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 どう切り出すのがいい? プロパティ? メソッド? 構造体?

Slide 31

Slide 31 text

struct var var var some init private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 Text Image resizable labelStyle 単純な View だから
 プロパティに切り出すのが
 よさそう

Slide 32

Slide 32 text

View の分割方法(変数) body: View { monsterLabel Label { (name) } icon { (iconName) . () }. (.monsterListCell) monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } var some let = : init Text Image resizable labelStyle padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 body 内に変数として定義することもできる 使われているのを見たことがないのと、 View を let で保持するのはよくなさそう

Slide 33

Slide 33 text

View の分割方法(ストアドプロパティ) monsterLabel Label { (name) } icon { (iconName) . () }. (.monsterListCell) body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } private let = : var some init Text Image resizable labelStyle padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 他のプロパティを参照しているとエラーになる
 cannot use instance member 'name' within property initializer; property initializers run before 'self' is available Apple のサンプルコードでも使われていないように見える

Slide 34

Slide 34 text

View の分割方法(コンピューテッドプロパティ) body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } var some init private var some : padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 Text Image resizable labelStyle Apple のサンプルコードでよく使われている しかしメソッドのみ使うという人も多そう

Slide 35

Slide 35 text

View の分割方法(メソッド) body: View { () . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } () View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } var some init private func -> some : monsterLabel padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 Text Image resizable labelStyle monsterLabel View に引数を渡す場合は
 プロパティでなくメソッドを使う

Slide 36

Slide 36 text

View の分割方法(構造体) body: View { ( : iconName, : name) . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } : View { iconName: name: body: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } var some init private struct var var var some : MonsterLabel iconName name padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 String String Text Image resizable labelStyle MonsterLabel 親 View のプロパティを直接参照できないので、
 単純な View だと冗長かもしれない View の外に出せるので、複数の View で利用する場合に使う

Slide 37

Slide 37 text

変数 ストアドプロパティ コンピューテッド
 プロパティ メソッド 構造体 使わない 使わない 引数なし、かつ単純な View
 引数あり、かつ単純な View 複数の View で利用する、または複雑な View View の分割方法まとめ

Slide 38

Slide 38 text

モディファイアの分割方法

Slide 39

Slide 39 text

struct var var var some init private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 Text Image resizable labelStyle

Slide 40

Slide 40 text

struct var var var some init private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 Text Image resizable labelStyle マテリアルな影を
 切り出したい View の extension? ViewModifier?

Slide 41

Slide 41 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle extension func -> some init { ( : CGFloat) View { ( : . ( : , : ), : elevation, : , : elevation ) } } View elevate elevation shadow color white 0 opacity 0.24 radius x 0 y View+Material 単純なモディファイアなので
 ViewModifier を使わないことにする

Slide 42

Slide 42 text

モディファイアの分割方法(extension) extension func -> some init { ( : CGFloat) View { ( : . ( : , : ), : elevation, : , : elevation ) } } View elevate elevation shadow color white 0 opacity 0.24 radius x 0 y モディファイアは View に生えているメソッドで、
 View を返す、とわかるといろいろ捗る
 イメージ View → モディファイアを適用 → 新しい View が返る なので extension でも基本的には同じことが実現できる

Slide 43

Slide 43 text

モディファイアの分割方法(ViewModifier + extension) モディファイアは View に生えているメソッドで、
 View を返す、とわかるといろいろ捗る
 イメージ View → モディファイアを適用 → 新しい View が返る extension func -> some private struct var func -> some init { ( : CGFloat) View { ( ( : elevation)) } } : ViewModifier { elevation: CGFloat ( : Content) View { content. ( : . ( : , : ), : elevation, : , : elevation ) } } View elevate elevation Elevate body content modifier Elevate elevation shadow color white 0 opacity 0.24 radius x 0 y 単純なモディファイアの場合、 ViewModifier を噛ませると 冗長になる

Slide 44

Slide 44 text

モディファイアの分割方法(ViewModifier + extension) モディファイアは View に生えているメソッドで、
 View を返す、とわかるといろいろ捗る
 イメージ View → モディファイアを適用 → 新しい View が返る extension func -> some private struct var func -> some init { ( : CGFloat) View { ( ( : elevation)) } } : ViewModifier { elevation: CGFloat ( : Content) View { content. ( : . ( : , : ), : elevation, : , : elevation ) } } View elevate elevation Elevate body content modifier Elevate elevation shadow color white 0 opacity 0.24 radius x 0 y 単純なモディファイアの場合、 ViewModifier を噛ませると 冗長になる ViewModifier は extension でラップすると モディファイアらしく使える ViewModifier 単体だと使いづらい

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

extension のみ ViewModifier のみ extension + ViewModifier 基本的にはこちらを使う 使わない(必ず extension でラップする) extension のみで実現できない場合に使う モディファイアの分割方法まとめ

Slide 47

Slide 47 text

View の分割場所

Slide 48

Slide 48 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle

Slide 49

Slide 49 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle View の分割場所はどうする? body? 同一 View? 同一ファイル? 別ファイル?

Slide 50

Slide 50 text

View の分割場所(body) body: View { monsterLabel Label { (name) } icon { (iconName) . () }. (.monsterListCell) monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : . ( : , : ), : , : , : ) ) } var some let = : init Text Image resizable labelStyle padding 16 background Color cornerRadius 3 shadow color white 0 opacity 0.24 radius 1 x 0 y 1 body 内だと変数として定義するしかない let で保持するのは避けたい

Slide 51

Slide 51 text

View の分割場所(同一 View) struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle 他で使わないなら 同一 View でよさそう

Slide 52

Slide 52 text

View の分割場所(同一ファイルの別 View) struct var var var some private struct var var var some : : View { iconName: name: body: View { ( : iconName, : name) . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } } : View { iconName: name: body: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView MonsterLabel String String MonsterLabel iconName name padding 16 background Color cornerRadius 3 elevate elevation 1 String String Text Image resizable labelStyle 単一の View のみで使うなら、View の外に出す必要はない エクステンションは構造体内に定義できないので出すしかない

Slide 53

Slide 53 text

View の分割場所(別ファイル) struct var var var some : : View { iconName: name: body: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterLabel String String Text Image resizable labelStyle 複数の View で利用するなら別ファイルがいい

Slide 54

Slide 54 text

body 同一 View 同一ファイル
 の別 View 別ファイル 使わない プロパティ、メソッド、単一の View で利用する構造体 同一ファイル内の複数の View で利用する構造体、
 単一の View で利用するエクステンション 複数の View で利用する構造体・エクステンション View の分割場所まとめ

Slide 55

Slide 55 text

モディファイアの分割是非

Slide 56

Slide 56 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle

Slide 57

Slide 57 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle .padding(_:) は 切り出したプロパティに含めないの?

Slide 58

Slide 58 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle .padding(_:) は 切り出したプロパティに含めないの? →外から決めるべきことは含めない パディングは Label 単体では決まらず、 配置場所によって決まる

Slide 59

Slide 59 text

struct func -> some extension where static var init : LabelStyle { ( : Configuration) View { ( : ) { configuration.icon . () . ( : , : ) configuration.title . (.title) . ( : . , : .leading) } } } Self == MonsterListCellLabelStyle { monsterListCell: MonsterListCellLabelStyle { . () } } MonsterListCellLabelStyle makeBody configuration LabelStyle HStack spacing 32 scaledToFit frame width 68 height 68 font frame maxWidth infinity alignment MonsterListCellLabelStyle

Slide 60

Slide 60 text

struct func -> some extension where static var init : LabelStyle { ( : Configuration) View { ( : ) { configuration.icon . () . ( : , : ) configuration.title . (.title) . ( : . , : .leading) } } } Self == MonsterListCellLabelStyle { monsterListCell: MonsterListCellLabelStyle { . () } } MonsterListCellLabelStyle makeBody configuration LabelStyle HStack spacing 32 scaledToFit frame width 68 height 68 font frame maxWidth infinity alignment サイズを固定する場合は切り出す MonsterListCellLabelStyle

Slide 61

Slide 61 text

struct func -> some extension where static var init : LabelStyle { ( : Configuration) View { ( : ) { configuration.icon . () . ( : , : ) configuration.title . (.title) . ( : . , : .leading) } } } Self == MonsterListCellLabelStyle { monsterListCell: MonsterListCellLabelStyle { . () } } MonsterListCellLabelStyle makeBody configuration LabelStyle HStack spacing 32 scaledToFit frame width 68 height 68 font frame maxWidth infinity alignment 左寄せにするのが目的で 外から決めるものではない MonsterListCellLabelStyle

Slide 62

Slide 62 text

・外から決めるべきなら切り出さない  ・例: .padding(_:)、.frame(width:height:) ・余白やサイズを固定したいなら切り出す モディファイアの分割是非まとめ

Slide 63

Slide 63 text

分割の上限

Slide 64

Slide 64 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle

Slide 65

Slide 65 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle .elevateBackground(elevation:) のようにまとめたほうがよくない?

Slide 66

Slide 66 text

struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } . (.monsterListCell) } } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable labelStyle .elevateBackground(elevation:) のようにまとめたほうがよくない? →そうかも どこまで分割するかは好みだけど、 body には「構造」と外から決めるものだけ残して 「装飾」は外出しすると見やすいかも

Slide 67

Slide 67 text

・どこまで分割するかは好み ・body には構造と外から決めるものだけ残すと
  読みやすい 分割の上限まとめ

Slide 68

Slide 68 text

View の「分割」まとめ

Slide 69

Slide 69 text

・Label など標準で用意されている View を活用する ・View を let で保持しない ・View の分割はスコープを最小にする ・body は「構造」のわかりやすさを意識する View の「分割」まとめ

Slide 70

Slide 70 text

02 View の「命名」

Slide 71

Slide 71 text

「View 単体の意味」と
 「各画面における役割」で分けて 考えるべき

Slide 72

Slide 72 text

View 単体の意味 Label Image Text

Slide 73

Slide 73 text

各画面における役割 画面全体(Page) 画面の一部分
 (Organism)

Slide 74

Slide 74 text

役割は1つ以上の View をラップする 単体 単体 単体 役割 単体 役割 単体 単体 単体 役割 単体 役割 役割 View に役割を付与するイメージ

Slide 75

Slide 75 text

実画面と設計の対応イメージ 単体 単体 単体 役割 単体 役割 役割 ヘッダー 画面の一部分 UINavigationBar UICollectionViewCell UICollectionViewCell UICollectionViewCell 画面全体

Slide 76

Slide 76 text

「View 単体の意味」の命名ルール

Slide 77

Slide 77 text

「View 単体の意味」の命名ルール プロトコルや型の名前を末尾に付ける struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable View プロトコルに準拠しているから「○○View」 型は View だが、 中身が Label のみなので「○○Label」

Slide 78

Slide 78 text

「View 単体の意味」の命名ルール Apple のサンプルコードや UIKit を参考にする(任意) struct var var var some private var some : : View { iconName: name: body: View { monsterLabel . ( ) . ( (.systemBackground) . ( ) . ( : ) ) } monsterLabel: View { Label { (name) } icon { (iconName) . () } MonsterListCellView String String padding 16 background Color cornerRadius 3 elevate elevation 1 Text Image resizable UICollectionViewCell のように使うので「○○CellView」

Slide 79

Slide 79 text

・プロトコルや型の名前を末尾に付ける ・Apple のサンプルコードや UIKit などを
  参考にする(任意) 「View 単体の意味」命名まとめ

Slide 80

Slide 80 text

「各画面における役割」の命名ルール

Slide 81

Slide 81 text

「各画面における役割」の命名ルール ルールを決めたら README に明記する View プロトコルに準拠しているから「○○View」

Slide 82

Slide 82 text

「各画面における役割」の命名ルール アトミックデザインなどを参考にする(任意) View プロトコルに準拠しているから「○○View」

Slide 83

Slide 83 text

「各画面における役割」の命名ルール 役割を細かく分け過ぎない View プロトコルに準拠しているから「○○View」 SwiftUI で ここまで分けると 冗長かもしれない

Slide 84

Slide 84 text

・ルールを決めたら README に明記する ・アトミックデザインなどを参考にする(任意) ・役割を細かく分け過ぎない 「各画面における役割」命名まとめ

Slide 85

Slide 85 text

View の「命名」まとめ

Slide 86

Slide 86 text

・View 単体の意味と、全体の役割を分けて考える ・全体の設計をドキュメント化する View の「命名」まとめ

Slide 87

Slide 87 text

まとめ

Slide 88

Slide 88 text

・言語化するのが大事 ・プロジェクトごとにルールをドキュメント化する ・できるところから適用してみてください ・今日の発表でわからなかったら
  何でも聞いてください! まとめ