SwiftUIの考察

WWDC 2019で個人的に一番驚いたのがSwiftUIの登場です。これまで[噂](https://daringfireball.net/2018/04/scuttlebutt_regarding_ui_project)はあったものの、今年の目玉はUIKit for Macだと思っていたのでびっくりしました。

SwiftUIがiOS開発にどのような影響を与えるのか考えてみました。

# SwiftUIの仕組み

SwiftUIは宣言型の構文（declarative syntax）で、Swift 5.1の[Embedded DSLs](https://developer.apple.com/videos/play/wwdc2019/402/)の機能を使って実装されています。具体的には[Opaque Result Type](https://qiita.com/koher/items/338d2f2d0c4731e3508f), [Implicit Return](https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md)や[Property Delegates](https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-delegates.md), [Function Builder](https://medium.com/swift2go/inside-swiftuis-declarative-syntax-s-compiler-magic-df9336d640f3)といった機能が使われています。

従来のUIKitやAppKitを使ったプログラミングは大きな問題がありました。それはUIの状態（ステート）と、その状態を表す変数を同期するのが難しかった事です。特に別スレッドによる処理（ネットワーク接続等）やアニメーションを使う場面で、UIの動きとステート変数を正しく同期させるのは至難の技です。

SwiftUIはこの問題を2つの仕組みによって解決します。ひとつは`@State`プロパティを使ってUIの状態とステート変数（[Souce of truth](https://developer.apple.com/videos/play/wwdc2019/204/)）を自動的に同期する仕組みです。ステート変数を変更するとUIが更新され、またUIを変更するとステート変数が更新されるようになり、開発者が同期する必要がなくなります。

もうひとつはUIの構築や更新を行うコードを1箇所にまとめることです。UI関連のコードはすべて`View`の`body`プロパティに記述することにより、従来のように複数箇所のコードが別々にUIの状態を監視したり変更したりする事がなくなります。またUIの変化をアニメーションで表現することも非常に簡単に行えるようになります。

SwiftUIは他にも`@Binding`や`BindableObject`, Combineフレームワーク等の仕組みがありますが、これらは別記事で紹介したいと思います。

# UIKit / AppKitとの関係

SwiftUIで宣言されたUIは、iOSではUIKit, macOSではAppKitを使って表示されます。ただし、宣言された通りのコントロールがそのまま1対1でUIKitやAppKitのビューに置き換えられるわけではなく、システム側で最適な表示方法に変換されます。例えば`View`の中の`Text`は`UILabel`で表示されず、`UIView`でプライベートAPIの[CGDrawingView](https://wwdcbysundell.com/2019/swiftui-relationship-to-uikit-appkit/)が使われて表示されます。また`VStack`や`HStack`もビューには変換されないため、パフォーマンスに影響はありません。

宣言型の構文の良いところはこのような実装を気にする事なくUIを構築できる点にあります。実行時にiOS, macOS, watchOS, tvOSでそれぞれ最適なビューで表現されます。またダークモードや言語、Dynamic Typesなどの環境が実行時に自動的に反映される点も、宣言型構文のメリットです。

SwiftUIはUIKitやAppKitのビューに混在させる事が可能です。SwiftUIで宣言されたビューは`UIHostingController`にラップして`UIViewController`として使うことができます。逆に、`View`の中に`UIView`を使う場合は`UIViewRepresentable`を使います。例えばSwiftUIではマップビューを宣言することができませんが、`UIViewRepresentable`を使って`MKMapView`を利用することが可能です。

# SwiftUIがもたらす影響

このような仕組みを持つSwiftUIですが、iOS開発者に与える影響も非常に大きくなります。

まずひとつはUIの更新やアニメーションなどの扱いがシンプルになるため、バグの少ないコードが書けるようになります。Swiftが`Optional`という概念を用いて`nil`に起因するバグを大幅に減らしたように、`@State`や`@Binding`などを使うことでUIの状態とステート変数の乖離に起因するバグやクラッシュなどが確実に減ると思われます。

また現在は`delegate`や`target-action`, `key-value-observing`などに混在している非同期処理も、Combineフレームワークを使うことによって一連の流れにまとめることができるようになり、バグを減らすことができるようになると思われます。やや学習コストは高いですが、SwiftUIやCombineフレームワークは開発者にとって強力なツールとなりそうです。

また従来のModel-View-Controller（MVC）のアーキテクチャにも影響がありそうです。MVCにおけるコントローラーの役割のひとつにモデルとビューの同期がありますが、SwiftUIでは前述の仕組みでこれが自動的に行われるからです。また画面遷移の場合も従来は`UIViewController`をpushしたりpresentしたりしていましたが、SwiftUIでは`destination`に新しい`View`を指定する形になり、コントローラが不要となります。

また`UIViewController`が担っていた`viewDidAppear`などのライフサイクルも、SwiftUIでは`View.onAppear()`に記述できるようになるなど、コントローラの役割が確実に減ってきています。

# SwiftUIの今後

SwiftUIは非常に革新的な技術ではありますが、まだ機能不足な点がいくつか見受けられます。例えばマップやコレクションビューを宣言的に使う事ができません。また`ScrollView`の`contentOffset`を監視したり更新したりすることもできません。拙作[カレンダーアプリ](https://firstseed.io/jp/)で実装している無限スクロールも難しそうです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">言われてみれば納得なんだけど、今のところSwiftUIでScrollViewのオフセットを監視したり、外から与えてProgrammaticallyにスクロール位置を変えたりはできない。 <a href="https://twitter.com/hashtag/WWDC19?src=hash&amp;ref_src=twsrc%5Etfw">#WWDC19</a></p>&mdash; kishikawa katsumi (@k_katsumi) <a href="https://twitter.com/k_katsumi/status/1137086830279516160?ref_src=twsrc%5Etfw">June 7, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

さらにSwiftUIはアーキテクチャに関わる仕様が抜け落ちています。仮に新規プロジェクトで全面的にSwiftUIを採用したとしても、これまで通り`UIApplicationDelegate`や`UIViewController`、`UIAlertViewController`等を使う必要があり、アップルはMVCを使い続けるのか、それとも別のアーキテクチャを採用するのかを開発者に示す必要があります。また複雑なジェスチャーには`UIGestureRecognizer`を使う必要があり、さらに複雑なレイアウトにはAutoLayoutを使う必要もあります。

今のSwiftUIはSwift 1.0の時のような衝撃がある一方で、未熟さも多々見られます。SwiftUIが今後どのように発展し、どこまで出来るようになるか注目していきたいと思います。

## 追記: Opaque Result Type

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">SwiftUIのsome Viewという部分は、その関数を呼ぶコードからはViewプロトコルとしてしか扱えないため実装上の型を隠蔽できるというメリットがあり、またコンパイラは本来の型（TextとかListとか）が分かっていて最適化もできるしパフォーマンスにも影響が出ない、といういいとこ取りのイメージ。</p>&mdash; tamadeveloper@Watchアプリできました (@tamadeveloper) <a href="https://twitter.com/tamadeveloper/status/1138062884766748677?ref_src=twsrc%5Etfw">June 10, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 参考

SwiftUI Tutorial 
https://developer.apple.com/tutorials/swiftui/tutorials

Embedded DSLs in Swift / What's New in Swift
https://developer.apple.com/videos/play/wwdc2019/402/ (31:15~)

Implicit returns from single-expression functions
https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md

Property Delegates
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-delegates.md

Source of truth / Introducing SwiftUI: Building Your First App
https://developer.apple.com/videos/play/wwdc2019/204/ (20:45~)

Data Flow Through SwiftUI
https://developer.apple.com/videos/play/wwdc2019/226/

Introducing Combine
https://developer.apple.com/videos/play/wwdc2019/722

Inside SwiftUI’s Declarative Syntax’s Compiler Magic
https://medium.com/swift2go/inside-swiftuis-declarative-syntax-s-compiler-magic-df9336d640f3

SwiftUI’s relationship to UIKit and AppKit
https://wwdcbysundell.com/2019/swiftui-relationship-to-uikit-appkit/

Swift 5.1 に導入される Opaque Result Type とは何か
https://qiita.com/koher/items/338d2f2d0c4731e3508f

SwiftUIでの画面遷移とプレゼンテーション
https://qiita.com/H_Crane/items/eb847ca7fb7a0b9e8073

一部の画面だけSwiftUIを使いたいとき
https://qiita.com/owen/items/73473cd2206afda3c5d4