これは Swift Advent Calendar 2019 24日目のエントリーです。
昨日23日は Moto0124 さんの CGAffineTransformを知る でした。
はてなエンジニアAdvent Calendar 2019での Deletable Table with TextField on SwiftUI に続いて、今回も SwiftUI ネタです。
そもそもSwift Playgroundsって?
ここでいう Swift Playgrounds
はXcodeに付属のPlaygroundではなく、iPadアプリとしての Swift Playgroundsを指しています。
以降、 Swift Playgrounds
は 「iPadアプリのSwift Playground」、 Xcode Playground
はXcodeに付属のPlaygroundと使い分けます。
ドキュメント: https://developer.apple.com/documentation/swift_playgrounds
SwiftUIをSwift Playgroundsで扱う
SwiftUIのアナウンス直後から、Swift Playground 3.1以降でSwiftUIが実行できることは話題になっていました。
Swift Playgrounds 3.1のVersion History
us: • Build with the SwiftUI framework in new playgrounds you create
jp: • 新しく作成するプレイグラウンドでは、SwiftUIフレームワークを使用してビルドできます
SwiftUIで表示してみる
すでに広く知られている話だとは思うけれど、まずは単純に表示してみましょう。
import SwiftUI import PlaygroundSupport struct ContentView: View { var body: some View { VStack { Text("Hello") .font(.largeTitle) .foregroundColor(.primary) Text("world") .font(.title) .foregroundColor(.secondary) } } } let host = UIHostingController(rootView: ContentView()) PlaygroundPage.current.liveView = host
Xcode Playground用ファイルとしてExportする
便利な使い方として、Xcode Playground用の拡張子 .playground
として作成することもできます。
ファイル作成時に Starting Points > Xcode Playground
を選択して、始める必要があります。
ここから作成したファイルをiCloudやAirdropでmacOSに送り、Xcodeで開くと、実行できます。
再利用できるViewの作り方
簡単なレイアウトのViewを作ってみるにはこれだけでも十分便利ですが、複雑なレイアウトを作ったり、再利用しやすいViewを作ったりには不向きに思えます。
また、Playground上での作成と言っても、最終的にはアプリ開発にViewを組み込みたいでしょう。
Swift Playgroundsでも、複数のSwiftファイルを扱うことができるので、各パーツのViewを別々のSwiftファイルに分割してみます。
参考: https://developer.apple.com/documentation/swift_playgrounds/structuring_content_for_swift_playgrounds/using_modules_to_share_code_in_a_playground_book
ファイルを追加
左上のファイルアイコンから、ファイルの追加ができます。
今回は3ファイル作成しました。
LargeTitleView.swift
import SwiftUI struct LargeTitleView: View { var body: some View { Text("Hello") .font(.largeTitle) .foregroundColor(.primary) } }
TitleView.swift
import SwiftUI struct TitleView: View { var body: some View { Text("World") .font(.title) .foregroundColor(.secondary) } }
Preview.swift
Preview
のみ public
修飾子でアクセスできるようにしておきます。
import SwiftUI public struct Preview: View { public init() {} public var body: some View { VStack { LargeTitleView() TitleView() } } }
main
Swift Playgroundのmainファイルでは、Previewを UIHostingController
で描画するようにします。
Swift Playgroundsでのmainと、その他のファイルとでは、別Module扱いになるため、 public
修飾子ないものにはアクセスできません。
import
はいらない。
import SwiftUI import PlaygroundSupport let preview = Preview() let host = UIHostingController(rootView: preview) PlaygroundPage.current.liveView = host
これ以降はPreviewと各Viewを書き換えるだけで、実行ができるようになりました。
こうすることで、各View毎のstructをそれぞれファイルとして分割できたり、 public
修飾子を書き忘れたり、 init()
実装し忘れで実行できなかったりがなくなります。
iCloud経由で、Xcodeのアプリプロジェクトに追加もできるので、非常に便利ですね。
もちろん、copy&pasteするだけでも、動きます。
Xcode Playgroundで実行する時には注意点があって、Xcode Playground上でのファイルリンクは特殊で、struct定義から直接ファイルジャンプはできません。
上部のペインから、辿ってください。
まとめ
iPad Proを買ってからというもの、Swift Playgroundsを使いたいと思うことが幾度とありましたが、やっと活用方法を見つけた気がします。
当たり前ですが、iCloudでファイルを同期する場合は、macOSで共有ディレクトリを開いていたりすると、最新の変更が更新されず、消えてしますこともあるので、ご注意を!
また、まだまだ不安定なのか、たまに .playground/Contents.swft
が破損して、開けなくなることもありました。 *1
おまけ
このエントリー用の検証していて、以下のコードを書けることに気づいた。
なにか使い道ないかな。
import SwiftUI public struct Preview<Body: View>: View { private let _body: Body public init(body: Body) { self._body = body } public var body: some View { _body } } let preview = Preview(body: ContentView())