SwiftUIの考察


WWDC 2019で個人的に一番驚いたのがSwiftUIの登場です。これまではあったものの、今年の目玉はUIKit for Macだと思っていたのでびっくりしました。

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

SwiftUIの仕組み

SwiftUIは宣言型の構文(declarative syntax)で、Swift 5.1のEmbedded DSLsの機能を使って実装されています。具体的にはOpaque Result Type, Implicit ReturnProperty Delegates, Function Builderといった機能が使われています。

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

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

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

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

UIKit / AppKitとの関係

SwiftUIで宣言されたUIは、iOSではUIKit, macOSではAppKitを使って表示されます。ただし、宣言された通りのコントロールがそのまま1対1でUIKitやAppKitのビューに置き換えられるわけではなく、システム側で最適な表示方法に変換されます。例えばViewの中のTextUILabelで表示されず、UIViewでプライベートAPIのCGDrawingViewが使われて表示されます。またVStackHStackもビューには変換されないため、パフォーマンスに影響はありません。

宣言型の構文の良いところはこのような実装を気にする事なく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の状態とステート変数の乖離に起因するバグやクラッシュなどが確実に減ると思われます。

また現在はdelegatetarget-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は非常に革新的な技術ではありますが、まだ機能不足な点がいくつか見受けられます。例えばマップやコレクションビューを宣言的に使う事ができません。またScrollViewcontentOffsetを監視したり更新したりすることもできません。拙作カレンダーアプリで実装している無限スクロールも難しそうです。

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

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

追記: Opaque Result Type

参考

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