AltSwiftUIさわってみた

iOSDC2020のセッションでAltSwiftUIという[ライブラリが紹介](https://www.youtube.com/watch?v=7ip7w1hCaz0)されました。

SwiftUIは導入するのにビルドターゲットをiOS13以降にする必要がありますが、このライブラリを使えばiOS11からSwiftUIライクにViewを組むことができます。

紹介されたときはこのライブラリが今後どう普及していくのか楽しみだったのですが、iOSDCが終わり数ヶ月経った現在もあまり導入例の記事やリポジトリがなかったため、少し触ってみることにしました。

この記事ではSwiftUIとの全ての違いを網羅することはできませんので、さらに気になられた方は[AltSwiftUIのリポジトリ](https://github.com/rakutentech/AltSwiftUI/tree/1.3.2)をクローンしExampleのプロジェクトを動かしてみることをお勧めします。~~この記事を読むよりそっちの方がよっぽど勉強になる~~

## 導入方法
CocoaPodsで導入することができます。

```Podfile
pod 'AltSwiftUI'
```

## 検証環境
iOS 12.0
Xcode12.2
AltSwiftUI 1.3.2

## 実際にViewを組んでみる

ボタンをタップするとテキストが表示されるだけの単純なviewを組んでみて、SwiftUIと実装にどのように差があるかみていきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290717/f7d3dd14-5b81-da8f-6eba-5e62467fb508.gif" width=240>
まずこちらがSwiftUIのコードです。あまりSwiftUI自体もキャッチアップできていないため、お作法がなっていないところもあるかもしれませんがご容赦ください🙏


```ContentView.swift
// SwiftUI
import SwiftUI

struct ContentView: View {
    @State private var isPushed = false
    
    var body: some View {
        VStack(alignment: .center) {
            Image("Image")
                .resizable()
                .scaledToFit()
                .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width)
            
            if isPushed {
                Text("Merry Christmas🎉")
                    .font(.system(size: 20, weight: .bold))
                    .padding(.bottom, 20)
            }
            
            Button(action: {
                isPushed.toggle()
            }, label: {
                Text("Button")
            })
            .accentColor(.orange)
            .frame(width: UIScreen.main.bounds.width - 24, height: 44)
            .font(.system(size: 14, weight: .bold))
            .overlay(
                RoundedRectangle(cornerRadius: 10)
                    .stroke(Color.orange, lineWidth: 2))
        }
        .offset(x: 0, y: -150)
    }
}

```

そしてこちらがAltSwiftUIのソースです。
ほぼ同じように実装できているように見えますが多少差があります。

```ContentView.swift
// AltSwiftUI
import AltSwiftUI

struct ContentView: View {
    var viewStore = ViewValues()
    @AltSwiftUI.State private var isPushed = false
    
    var body: View {
        VStack(alignment: .center) {
            Image("Image")
                .resizable()
                .scaledToFit()
                .frame(width: UIScreen.main.bounds.width , height: UIScreen.main.bounds.width)
            
            if isPushed {
                Text("Merry Christmas🎉")
                    .font(.system(size: 20, weight: .bold))
                    .padding(.bottom, 20)
            }
            
            Button(action: {
                isPushed.toggle()
            }, label: {
                Text("Button")
            })
            .accentColor(.orange)
            .border(.orange, width: 2)
            .font(.system(size: 14, weight: .bold))
            .frame(width: UIScreen.main.bounds.width - 24, height: 44)
            .cornerRadius(10)
        }
        .offset(x: 0, y: -150)
    }
}

```

ではここからはいくつかの違いについて触れていきます。

### viewStore
AltSwiftUIは`viewStore`の実装を求められます。
求められるものは書いておきましょう！笑

### someの有無
SwiftUIではViewの前に`some`をつけたものを`body`に準拠させていますが、AltSwiftUIではつける必要がありません。つける必要がないというのは正確ではなく、つけることができないという方が正しいです。

`some`はSwift5.1から使えるようになったものなので、iOS11をサポートしようとすると使えません。この`some`については詳しくは[こちら](https://qiita.com/masa7351/items/1e6b235c1c0d3f54b3de)の記事に書かれています（あまり理解していないのでどなたか教えてください・・・）

### Property Wrapper
AltSwiftUIではSwiftUIのProperty Wrapperも使用できます。使う際は`@AltSwiftUI.State`のように指定する必要があります。最新のリリースをみるとこの指定がなくても直接`@State`で使えるようだったのですが、なぜかエラーで使えませんでした。わかり次第、追記します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290717/d699e914-b8e9-8d13-2649-d971a29e13d3.png" width="320">

### RoundedRectangleが使えなかった
ボタンの角丸を表現するのにSwiftUIでは`RoundedRectangle`を使用したのですが、AltSwiftUIでは使えませんでした。しかし`border`と`cornerRadius`を使えば表現できるので、さほど問題でもないかなと思います。

他をあまり調べていませんが、こういった差はちょこちょこありそうなので使う時に注意が必要かもしれません。


## Preview機能は？
SwiftUIというとPreview機能が目玉のひとつでしょう。コードの変更がリアルタイムでViewに反映されるため、ずいぶんと実装が楽になりました。

AltSwiftUIでPreviewを実現するためには、SwiftUIとAltSwiftUIそれぞれの`PreviewProvider`を準拠させる手法が公式から紹介されています。実際この方法でPreviewすることができました。

```Preview.swift
#if DEBUG && canImport(SwiftUI)
import SwiftUI
import protocol SwiftUI.PreviewProvider
import protocol AltSwiftUI.View

struct MyPreview: AltPreviewProvider, PreviewProvider {
    static var previewView: View {
        ContentView()
    }
}
#endif
```


## まとめ
さわってみた感想としては

* SwiftUIとほぼ同様なシンタックスで宣言的にコードがかけるのが良い
* SwiftUIに移行する時ラクかも？
* 予期せぬバグなどは心配なので最小のコンポーネントから試すのがよさそう

と思いました。一方これからSwiftUIが普及していく中で、わざわざAltSwiftUIを使う機会は限定的かな？とも感じました。しかし携わるプロジェクトによってはiOS12以前をサポートしなければならない場面も存在すると思います。そういった方がSwiftUIを実務で取り入れつつ学ぶにはよいかもしれません。