ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Xcode PlaygroundにおけるPackageとProject
Xcode Playgroundがあれば、デベロッパはSwiftとフレームワークAPIをより調査しやすくなり、迅速な実験のためのscratchpadとしても使用できます。Xcode PlaygroundにおけるXcodeのモダンなビルドシステムの活用、リソースへの改善されたサポートの提供、プロジェクト、フレームワーク、Swiftパッケージの統合など、ドキュメントや開発ワークフローをどのように改善できるかをお伝えします。
リソース
関連ビデオ
WWDC23
WWDC20
WWDC19
-
ダウンロード
こんにちは WWDCへようこそ “Xcode Playgroundsで PackagesとProjectsを深める” 本セッションへ ようこそお越しくださいました 私はクリス・マイルズ Xcodeチームのエンジニアマネージャです デベロッパはXcode Playgroundsを使って APIとフレームワークの新しいアイディアを いろいろと試してみたりすることが大好きです 今日はXcode 12が 最適化や改善を通じて Playgroudsがシームレスに Swiftパッケージおよびフレームワークと 連動可能になったことをお話しします Xcode 12ではPlaygroudsは 最新の構築システムに 完全に統合 そして連携されています この統合で多くの改善がなされ パッケージやプロジェクトやリソースと うまく実行できるように強化されています まずSwiftパッケージで このPlaygroudsがいかに相性よく動作し 実行可能なコードを含む パッケージドキュメントを作るのに すばらしい選択肢となりえるか お見せいたします そしてフレームワークやパッケージ依存性などの プロジェクトターゲットの 改善されたPlaygroudsのインポート機能を デモでお見せします そして プレゼンテーションの最後に 構築のサポートが必要なリソースとの連携 例えばMLモデルやアセットカタログなどを Playgroudsで実行するデモをお見せします では 始めます まずXcode PlaygroudsとSwiftパッケージの 相性の良さから見ていきましょう ここでNutritionFactsと呼ばれる Swiftパッケージをダウンロードして準備します “package.swift”をダブルクリックし Xcodeで開きます NutritionFactsは主な食品について 正確な栄養成分表を提供してくれます レシピや食品アプリケーションの中では とても使い勝手のよいパッケージです NutritionFactsのすばらしいところは 作成者が盛り込んだ 豊富なドキュメンテーションには APIの使い方も説明されており さらにプレイグラウンドとして 書かれているのです Playgroudsはドキュメンテーションと 非常に相性が良く― シームレスにXcode 12のパッケージと 連携しています それでは早速見てみましょう
ナビゲーターに入り Playgroudsフォルダを開きます そしてNutritionFacts プレイグラウンドを選びます ここではパッケージの記述が表示され その次に詳しいAPIの使い方が ドキュメンテーションで示されており そしてスニペットコードが例示されます プレイグラウンドなので そのコードを実行できます ではツールバーの“実行”を 押してやってみましょう プレイグラウンドが実行され 右側ではライブビューが見られます このビューはパッケージが提供しています お馴染みのフォーマットで 栄養成分が表示されています
エディターでは識別子ごとのアイテムを検索し 該当する栄養成分を読み出します プレイグラウンドは右側の結果バーに ステートメントの結果を表示するので コードが何なのかも理解できます
このようにXcode 12内でPlaygroudsは パッケージとシームレスに連携します これが可能になったのは 最新の構築システムと Playgroudsが統合されたからです これによりプレイグラウンドコードに パッケージがインポートできるようになりました ドキュメンテーションを 書かなければならない時や Swiftパッケージのチュートリアルを 作成しなくてはならない時には プレイグラウンドをお使いください ユーザーが例コードを実行しプレイグラウンドで ライブで結果を見られるという利点があります では次にXcode 12のおかげで いかに簡単にPlaygroudsが プロジェクトからのフレームワークや パッケージ依存を使うかをお見せします
ここではFrutaというプロジェクトを使います Xcode内で開けてシミュレーターで プロジェクトを実行しましょう Frutaはスムージーのブラウジングと 注文のできるiOSとMacのアプリケーションです Apple Payでレシピの購入もでき ご自宅で自分だけの スムージーを作ることもできます ではシミュレーターで アプリケーションを見てみましょう 今オーダーできる スムージーのリストが出てきます もし1つを選ぶと Apple Payで購入するオプションが出てきます スクロールすると スムージーの成分が出てきます 1つ選んで詳しく写真を見てみましょう “I”ボタンをタップして 成分の栄養情報を見てみましょう ここでNutritionFactsパッケージから表示される ビューであることにお気づきかもしれません このプロジェクトは パッケージに依存性があるからです 左側のナビゲーターでもご覧になれます
プロジェクト依存性があるものの パッケージ内のどのプレイグラウンドでも 使うことができます ではパッケージを開封して 先ほど開封した NutritionFactsプレイグラウンドを開きましょう ここではコンテンツは同じです そして前と同様プレイグラウンドを実行して 結果を見ることができます
なのでパッケージの中のプレイグラウンドは 先ほどやったように直接開封しようと プロジェクトの一部でパッケージ依存であろうと いずれでも使うことができます パッケージ依存性のある すべてのファイルと同様 プレイグラウンドはリードオンリーです このプロジェクトにはUtility Viewsと呼ばれる フレームワークも入っています プロジェクトエディターを見ましょう ここではUtility Viewの フレームワークターゲットが見られます フレームワークが同じワークスペースにある限り それをPlaygroudsの中で使うことが 以前と比べて簡単になっています 1つの例をお見せしましょう ナビゲーターでPlaygroudsフォルダを開封します そしてSmoothieLabと呼ばれる プレイグラウンドを見ることにします ではSmoothieLabを開けましょう 上部にビルドアクティビティがあることに 注目してください これはBuild Active Schemeと呼ばれるXcode 12の 新しいプレイグラウンドオプションです ではインスペクターを開いて見てみましょう ここにプレイグラウンドの設定があります 設定の下部に 新規オプションとして追加されました “Build Active Scheme”を有効にすると Xcodeにその時点で 選択されているスキームの中で 自動ですべてのターゲットを構築し どのモジュールでもプレイグラウンドへ インポート可能にするよう指示します これがプレイグラウンドを開封した時に Xcodeがその時点で選択されているスキームの ターゲットをすべて構築し プレイグラウンドが Utility Viewsフレームワークと NutritionFactsパッケージを インポートできる理由なのです SmoothieLabは新しいスムージー作りのための ユーティリティプレイグラウンドなのです ここで原材料を選ぶことができ またさまざまな量でそれらを組み合わせ 栄養価を精査することができます カロリー成分分けチャートも作成できますが それにはUtility Viewsフレームワークの チャートビューを使います ではこのスムージーのカロリー成分を見るため プレイグラウンドを実行しましょう
ライブビューでは カロリー分布チャートが見れます ここで脂肪分は25%未満に抑えたいものです ではチャートをタップして 脂肪の割合を見てみましょう この場合は33%です ピーナッツバターの量を 減らせばいいかもしれません 後にしましょう 見ての通りプレイグラウンドは とても使いやすいツールで 栄養バランスのとれたスムージーを作る際 素材の組み合わせをしやすくしてくれます まだ味がどれほど良いのかを計算する アルゴリズムは設計できていませんが 味見は大好きなので 良いことにしましょう Playgroudsはフレームワークや パッケージのようなプロジェクトターゲットと シームレスに運用できるのです Xcodeはこれらの ターゲット構築をしてくれるので プレイグラウンドコードへ インポートができるのです 忘れずに“Buid Active Scheme”を 有効にしてください しかもうれしいことにデフォルトで このオプションは有効化されています またインポートしたいモジュールは アクティブスキームの一部であるか そのスキームが構築した別のターゲットに 依存性を持っているかの いずれかにしてください Xcode 12の強化された点はまだあります それはプレイグラウンドのための フル構築ログが使えるようになったことです SmoothieLabの構築ログを見てみましょう レポートナビゲーターへスイッチします 上部にSmoothieLabプレイグラウンドの 構築ログがあるでしょう ではそれを選んで 構築ログを開けてみましょう ここにはアクティブスキームの一部として 構築されたターゲットの構築の詳細と サポートするために構築された モジュールの詳細を見ることができ プレイグラウンド内でコンパイルされた ソースとリソースも含まれています 構築ログは構築の問題がある時の 診断には欠かせません プレイグラウンドでも使えるということは とてもうれしいことです 更にこのPlaygroudsとXcodeの モダンビルドシステムの統合がもたらした恩恵は ビルドシステムにサポートされている 全てのリソースタイプが Playgroudsでサポートされている点です それではプレイグラウンドの中でいくつかの リソースを使った例をお見せしましょう これらはビルドシステムサポートが必要です
Frutaプロジェクトへ戻り フィーチャーを追加したいと思います ユーザーが自分のiPhoneのカメラを 好みの果物に向けると Frutaはまるで魔法のように その果物に合ったスムージーを提案します これをするためには機械学習を使い 物体検出と画像分類を実行したいと思います ここでは多数の分類子が使えますので サンプル画像が数枚あるこれを使って タスクにフィットするか 見てみようと思いますが それをするためにXcodeにある プレイグラウンドを使おうと思います まず果物の画像を数枚選びテストしてみますが たまたまこのプロジェクトには 使える画像があるようです ではここでシミュレーターにスイッチし ナビゲートして戻りましょう このオレンジの画像は見ましたよね? ここでそれを無視すると それ以外の成分の画像が出てきます ではプロジェクトの中で これらを見つけましょう シミュレーターはここでは必要ないので隠して 共有フォルダを拡大しましょう そしてアセットカタログを見てみましょう アプリケーションのアイコンと色が見えますし 成分フォルダの中では スムージーに入れる材料を表示した 画像が見えます これは我々の機械学習モデルをテストする 良いサンプルだと思います ではデスクトップへドラッグして コピーを作りましょう
もうプロジェクトを開いておく 必要はないので閉じましょう その代わりに“File”と“New Playground”で 新しいプレイグラウンドを作りましょう
iOSのブランクプレイグラウンドを使います
これをML Frutaと名前をつけて デスクトップに格納しましょう
ここでウインドウのサイズを大きくして 作業スペースを作ります アセットカタログをドラッグして リソースフォルダへ持っていきましょう ここでPlaygroudがアセットカタログファイルを コンパイルし その結果コードからリソースへアクセスが できるようになったことに着目してください UIImage(named を使ってもできます
アセットカタログ内のフォルダ名と 画像の名前を指定してください ではここでプレイグラウンドを実行して 結果を見ましょう 結果の欄には画像の幅が表示されており QuickLookで画像を見る時は プレビューを見ます 今このアセットカタログから画像への アクセスがあるということです ここでは機械学習モデルだけが必要です ではずっとブラウジングをしている Safariへスイッチします SafariはApple Developerウエブサイトの 機械学習セクションでブラウジングをしています CoreMLModelのページの中では 数多くの画像分類モデルや物体検出モデルを 見ることができます ここではYOLOv3モデルを試してみます 1つのシーン内で複数の物体を検出でき 各々の物体を分類できます これは果物の入ったボウルにカメラを向け シーン内にある全ての果物を特定させるのに 最適だと思います では“View models”をクリックすると トップに最高精度モデルをダウンロードできます 時間が限られているため省略しますので Safariを最小化します
デスクトップのモデルをドラッグして プレイグラウンドの リソースセクションへと持っていきます Xcodeが自動でモデルを コンパイルしてくれますので 私たちはその間にモデルを選んで エディターの詳細を見てみましょう トップの近くにあるモデルクラスが 重要な詳細情報となります これはクラスの名前を表示してくれて 私たちのSwiftコードで使う ビルドシステムにより 自動で生成されます ではそれを使ってみましょう プレイグラウンドへ戻って CoreMLをインポートするために コードを追加しましょう それから可変yoloModelを呼び出します そして自動で生成された クラスYOLOv3を使用して デフォルトコンフィギュレーションで 新しいインスタンスを生成します それからモデルプロパティを呼び出します ではプレイグラウンドを実行し 結果を見ましょう
プレビューが見れるモデル記述へ戻り インプット アウトプットや その他プロパティの詳細を見ます 機械学習モデルとテスト用画像が手元にあるので あとはこれを組み合わせるだけです Visionフレームワークを使うと簡単です このコードを入れ替えその代わりに Visionフレームワークをインポートします 3つの成分でアレイを定義し テストを開始します そうすると選んだyoloModelが 使えるようになり その結果VisionフレームワークのCoreMLModelの インスタンスを作ることができます そしてようやくCore MLリクエストを 作成することができます このCoreMLRequestを使い画像にする 機械学習リクエストを作成することができます ではリクエストの結果を可視化し 画像をお見せしましょう そして画像の中で検出された物体の周りに 四角の箱を描きましょう 過去使ったコードを用意しました QuickLookでざっとお見せしましょう これは私たちが行った物体検出の 結果を保持するストラクト数個と 私たちの画像を表示するために使う SwiftUIビューを定義し 画像の中で検出した物体を 四角の箱を描いて囲みます このコードについての詳細は セッションのウェブサイトで 見られるようにします サポートコードをプレイグラウンドの ソースセクションへドラッグしたら Xcodeがコンパイルしてくれますので コードが使えるようになります
成分の名前を反復し アセットカタログから その各々に対し画像を呼び出します そしてその画像で 画像リクエストハンドラーを作ります これを使い 機械学習リクエストを 実行することができます そこからは結果をパスするだけです 各結果は RecognizedObjectObservation です 各観察例を数値化し 最も信頼度の高い該当するラベルと 画像内の囲みボックスのついた物体とを呼び出し それらを RecognizedObject として返します 最後に各画像に対し 結果を ObjectDetectionResult として返します そして下の部分に表示される 結果を見てみましょう Xcodeは3枚の画像を反復しており その結果を見ることができます 確かに各画像の結果が出ているので それを可視化してみましょう これはプレイグラウンドのLiveViewを RecognizedObjectVisualizer に設定し そして結果をパスします さて実行を継続しましょう
右側には物体検出の結果が見れます 最初にパスした画像はバナナです オレンジ色はバナナが100%の正確さで 検出されたことを示しています かなり良い結果ですね 次の画像は3個のオレンジですが 3つとも高い信頼度をもって検出されています うまく実行できたということです
3番目の画像は アーモンドミルク入りのボトルです 機械学習モデルは高い信頼度で ボトルを検出したことがわかりますが それがミルクかアーモンドミルクかの レベルまでの 特定した分類はできませんでした このモデルはボトルを検出する学習はしましたが それがミルクかアーモンドミルクかを検出し 特定する学習はしていないからです 学習をしたものに対しては とても良いパフォーマンスをしています 私たちのアプリケーションに使えるでしょう 次のステップでは モデルをさらに学習させ― 栄養成分のタイプを 検出できるようにします MLモデルの学習についての情報は WWDC 2019の“Training Object Detection Models in Create ML”をご覧ください
Playgrouds内でビルドシステムサポートが 必要なリソースの使用例でした ではプレゼンテーションをまとめましょう Xcode 12では最新のビルドシステムと 完全に統合連携されています そして“Build Active Scheme”を有効にすると 選択されたスキーム内の 全ターゲットは自動で構築され 同じワークスペースにあるプレイグラウンドへ インポート可能になります どんなフレームワークやSwiftパッケージでも 関係ありません
ビルドシステムが認識できる全リソースタイプは Playgroudsにて使えるようになっています 例えば私がデモをお見せした アセットカタログやMLモデルがその例です
フルプレイグラウンドビルドログは レポートナビゲーターでご活用になれます プレイグラウンドのビルドの時に起こる 問題の診断にとても便利です
Playgroudsはパッケージやフレームワークの ドキュメント化をする際に役に立ちます そしてさらにプレイグラウンドは コーディングしたりユーティリティコードを 書く時にも便利です プレイグラウンドが皆さんにどう役立っているか ぜひともお聞かせください ありがとうございました
-
-
8:53 - Playgrounds and resources Demo: Part 1
import UIKit let image = UIImage(named: "ingredient/orange")
-
10:18 - Playgrounds and resources Demo: Part 2
import CoreML let yoloModel = try YOLOv3(configuration: MLModelConfiguration()).model
-
10:54 - Playgrounds and resources Demo: Part 3
import UIKit import CoreML import Vision let ingredientNames = [ "banana", "orange", "almond-milk", ] let yoloModel = try YOLOv3(configuration: MLModelConfiguration()).model let model = try VNCoreMLModel(for: yoloModel) let request = VNCoreMLRequest(model: model) {_,_ in }
-
11:24 - Recognized Object Visualizer
import Foundation import SwiftUI import UIKit // MARK: Model /// The result of object detection on an image. public struct ObjectDetectionResult : Identifiable { public var name: String public var image: UIImage public var id: String public var objects: [RecognizedObject] public init(name: String, image: UIImage, id: String, objects: [RecognizedObject]) { self.id = id self.name = name self.image = image self.objects = objects } } /// An object recognized by an image classifier. public struct RecognizedObject : Identifiable { public var id: Int public var label: String public var confidence: Double public var boundingBox: CGRect public init(id: Int, label: String, confidence: Double, boundingBox: CGRect) { self.id = id self.label = label self.confidence = confidence self.boundingBox = boundingBox } } // MARK: Views public struct RecognizedObjectVisualizer : View { public var results: [ObjectDetectionResult] public var imageSize: CGFloat = 400 public init(withResults results: [ObjectDetectionResult]) { self.results = results } public var body: some View { List(results) { result in Spacer() VStack(alignment: .center) { RecognizedObjectsView( image: result.image, objects: result.objects ) .frame(width: imageSize, height: imageSize) Text(result.name.capitalized) Spacer(minLength: 20) } Spacer() } } } struct RecognizedObjectsView : View { var image: UIImage var objects: [RecognizedObject] var body: some View { GeometryReader { geometry in Image(uiImage: image) .resizable() .overlay( ZStack { ForEach(objects) { object in Rectangle() .stroke(Color.red) .shadow(radius: 2.0) .frame( width: object.boundingBox.width * geometry.size.width / image.size.width, height: object.boundingBox.height * geometry.size.height / image.size.height ) .position( x: (object.boundingBox.origin.x + object.boundingBox.size.width / 2.0) * geometry.size.width / image.size.width, y: geometry.size.height - (object.boundingBox.origin.y + object.boundingBox.size.height / 2.0) * geometry.size.height / image.size.height ) .overlay( Text("\(object.label.capitalized) (\(String(format: "%0.0f", object.confidence * 100.0))%)") .foregroundColor(Color.red) .position( x: (object.boundingBox.origin.x + object.boundingBox.size.width / 2.0) * geometry.size.width / image.size.width, y: geometry.size.height - (object.boundingBox.origin.y - 20.0) * geometry.size.height / image.size.height ) ) } } ) } } }
-
11:48 - Playgrounds and resources Demo: Part 4
let results = ingredientNames.compactMap { ingredient -> ObjectDetectionResult? in guard let image = UIImage(named: "ingredient/\(ingredient)") else { return nil } let handler = VNImageRequestHandler(cgImage: image.cgImage!) try? handler.perform([request]) let observations = request.results as! [VNRecognizedObjectObservation] let detectedObjects = observations.enumerated().map { (index, observation) -> RecognizedObject in // Select only the label with the highest confidence. let topLabelObservation = observation.labels[0] let objectBounds = VNImageRectForNormalizedRect(observation.boundingBox, Int(image.size.width), Int(image.size.height)) return RecognizedObject(id: index, label: topLabelObservation.identifier, confidence: Double(topLabelObservation.confidence), boundingBox: objectBounds) } return ObjectDetectionResult(name: ingredient, image: image, id: ingredient, objects: detectedObjects) } results
-
12:33 - Playgrounds and resources Demo: Part 5
import PlaygroundSupport PlaygroundPage.current.setLiveView( RecognizedObjectVisualizer(withResults: results) .frame(width: 500, height: 800) )
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。