[SwiftUI]SwiftUIの記法をSwiftらしく理解する。

[SwiftUI](https://developer.apple.com/jp/xcode/swiftui/)はこれまでのUIKitを用いたUIの作成に変わる新たな方法としてWWDC 2019で発表されたフレームワークです。最近ではWidgetがSwiftUIでないと作れなかったりと、将来的にはSwiftUIを用いた開発が主流になっていくと思います。
自分もだいぶ前からSwiftUIの勉強をなかなか気乗りせずにいましたが、Widgetを期に本格的に勉強を始めました。。

↓有限不実行のツイート

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">そろそろSwiftUIマジで本格的に勉強しよう</p>&mdash; M (@p_x9) <a href="https://twitter.com/p_x9/status/1280851389564903427?ref_src=twsrc%5Etfw">July 8, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


自分がSwiftUIを敬遠してしまっていたのは、コードを見て__「SwiftUIはSwiftじゃない別の言語にしか見えない」__と思ってしまったのが理由です。しかし、よく調べたり内部実装を紐解いていくうちに、__「SwiftUIはやはりSwiftだ」__と理解することができました。

そこで今回はSwiftUIの記法に違和感を覚えたが、それを__Swiftらしく__理解できた点についていくつか書いていきます。


##1,returnの省略
まず以下のコードを見てください。

```swift:
private struct ContentView: View {
    var body: some View{
        Text("test")
    }
}
```
これにはSwift5.1から新たに使用可能となった記法が使われています。これまでの記法で表せば、上記のコードは以下のように書けます。

```swift:
private struct ContentView: View {
    var body: some View{
        return Text("test")
    }
}
```
つまり、__getterを記述する際に単一式を返す場合、returnを省略できる__ということです。前述の通りこれはSwift5.1からの機能であるので、__SwiftUIに限らずこれからは通常のSwiftでも使用できる記法__です。

##2,プロパティの型指定をプロトコルで指定できる。
SwiftUIにおける”View”はUIKitのUIViewとは違い、classやstructではなくプロトコルです。![スクリーンショット 2020-12-29 0.12.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/fc71e5ba-dc0b-ba34-cae3-265d1d353979.png)
これまでプロパティの型を「〇〇プロトコルに適合した何かしらの型」というように柔軟に指定することはできませんでしたが、これもSwift5.1から新しくできるようになりました。型にプロトコルを指定する際、プロトコル名の前に__some__を記述します。上記のコードではbodyプロパティがその例です。
以上を踏まえれば以下のような意味のないコードも書けるということです。

```swift:
var test: some Equatable = 1
```

##3,PropertyWrapper
SwiftUIを触っていると、プロパティの宣言varの前に`@State`やら`@Binding`,`@Environment`などというものが出てくると思います。これもまたSwift5.1からのプロパティラッパーという機能です。これはgetterとsetterにある共通の処理がある場合、プロパティラッパーを定義してその__getterやsetterの共通の処理を適宜する__という機能です。

例えばUserDefaultsに保存されている値を扱う際、値を取得(get)する際にはUserdefaultから読み出す処理、値をセットする際にはUserDefaultsに保存する処理を書く必要があります。UserDefaultsを保存された値を複数扱う場合、その取得・保存の処理をそれぞれ逐一記述するのは面倒です。こういった場合にPropertyWrapperが有用なわけです。([このコードを参照](https://github.com/batuhansk/UserDefault/blob/master/Sources/UserDefault/UserDefault.swift))

##4,関数の引数のうち一番最後のクロージャは外に出せる
HStack,VStackやForEachなどはこの、クロージャは外に出せることを利用して記述しています。

例えば以下のコードがあったとします。

```swift:
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {_ in 
    print("cancel")
})
```

この時,引数のクロージャは、外に出して以下のように記述することができます。

```swift:
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel){_ in
    print("cancel")
}
```

さらに__引数がクロージャのみの場合は以下の様に（）も省略できます__。

```swift:
//通常の書き方
var array = [Double](stride(from: 0, to: 1, by: 0.1))
array = array.map({
    sin($0)
})
//クロージャを外に出した書き方
var array = [Double](stride(from: 0, to: 1, by: 0.1))
array = array.map{
    sin($0)
}
```

加えて以下のようにクロージャの引数が複数ある場合はどうでしょう。

```swift:
UIView.animate(withDuration: 6, animations: {
     self.view.backgroundColor = .cyan
}, completion: {
     print("completed:\($0)")
})
```

クロージャが複数ある場合は__最後のクロージャのみ__外に出すことができます。

```swift:
UIView.animate(withDuration: 6, animations: {
      self.view.backgroundColor = .cyan
}){
      print("completed:\($0)")
}
```

##5,Function Builder
HStackやVStackのこの構文にはかなり違和感を覚えると思います。クロージャ内に要素を羅列するだけでそれらがよしなに扱われてしまうのには自分もかなり違和感を覚えました。

```swift:
VStack{
   Text("1")
   Text("2")
   Text("3")
}
```
こちらもまたSwift5.1からの新機能__ファンクションビルダ__を使用して実装されているそうです。ファンクションビルダを使用すれば、引数を羅列するだけで任意のインスタンスを作成できます。

例えば以下の構造体があったとします。

```swift:
struct Person{
   let id:Int
   let name:String
}
```
この時ファンクションビルダを作成します。ビルダの作成は`@_functionBuilder`属性を指定した構造体を作成し、buildBlock関数を定義することで作成できます。

```swift
@_functionBuilder struct PersonBuilder{
   public static func buildBlock(_ id:Int,_ name:String)->Person{
      .init(id: id, name: name)
   }
}
````
このビルダをしようすると以下のような引数を羅列するだけでインスタンスを生成できる関数が作成できます。

```swift
@PersonBuilder func test()->Person{
   1
  "Linus Benedict Torvalds"
}
```

これは関数やイニシャライザの引数にクロージャとして使用することもできます。実際にHStackやVStackなどのイニシャライザの引数には`@ViewBuilder`のクロージャが存在しており、これを利用して前述のような初見では違和感のある記法が出来るようにされています。

```swift
func test(@PersonBuilder closure:()->Person)->Person{
   return closure()
}
```

##最後に
これからも何か気づきがあれば更新していきます。ご拝読ありがとうございました。


##参考
https://github.com/batuhansk/UserDefault
https://qiita.com/yimajo/items/32a5209bd73580d283a1
https://www.vadimbulavin.com/swift-function-builders-swiftui-view-builder/