XcodeとPlaygroundsを共存させるプロジェクト構成
はじめに
iPadでのiOSアプリ開発
これはiOSエンジニアとしてロマンの一つだと思います。
Swift Playgrounds の機能も増えてきており、iPadで開発を完結させることも不可能ではなくなってきました。
実は Capabilities などの設定もPlaygroundsで行えるため、本当に様々な機能を持ったアプリを作ることができます。
ただ残念なことに全ての Capabilities を設定できるわけではありません。
例えば iCloud 関連の設定はできないようになっています。
他にも
- Info.plistで詳細な設定を行いたい
- Run Scriptを設定したい
- Xcode Cloudと連動させたい
など、Xcodeなら簡単に出来ることが、Playgroundsでは難しい場合があります。
そこで、本記事ではXcodeとPlaygroundsを共存させるプロジェクト構成を紹介します。
環境
- Xcode 15.4
- Swift Playgrounds 4.5
- iOS 17.5
- Xcode Cloud
全てが必須条件ではないですが、制約が厳しくない状況下という点が伝われば嬉しいです。
方針
Playgroundsプロジェクトのコードをパッケージ化し、Xcodeプロジェクトから利用する構成
を目指します。
手順
Xcodeプロジェクトの作成
- Xcodeプロジェクト
Sampleを作成します。
![]() |
![]() |
![]() |
-
Hello, world!の代わりにXcodeと表示するように変更します。 - Xcodeプロジェクトを実行して
Xcodeが表示されることを確認します。 - Xcodeプロジェクトを閉じます。
Playgroundsプロジェクトの作成
- Playgroundsプロジェクト
Sampleを作成します。
![]() |
![]() |
![]() |
-
Hello, worldの代わりにPlaygroundsと表示するように変更します。 - Playgroundsプロジェクトを実行して
Playgroundsが表示されることを確認します。 - Playgroundsプロジェクトを閉じます。
ここで改めてとなりますが、
Xcodeプロジェクトを実行した時に Playgrounds が表示される
が今回実現したいこととなります。
Xcodeワークスペースの作成
- Xcodeでワークスペース
Sampleを作成します。 - 空のサイドバーに
Sample.xcodeprojをドラッグ&ドロップします。
![]() |
![]() |
![]() |
パッケージの作成
- 空のパッケージ
MyPackageを作成し、Add to,Groupを設定します。
![]() |
![]() |
![]() |
-
MyPackage配下にSample.swiftpmを配置します。 -
Sample.swiftpm配下にSourcesディレクトリを作成し、ContentView.swiftをSourcesに移動します。
Sample/
├── Sample.xcworkspace
├── Sample.xcodeproj
├── Sample/
│ ├── SampleApp.swift
│ └── ContentView.swift
└── MyPackage/
├── Package.swift
└── Sample.swiftpm
├── Package.swift
├── MyApp.swift
└── Sources/
└── ContentView.swift
-
MyPackageのPackage.swiftを設定します。
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MyPackage",
platforms: [.iOS(.v17)],
products: [.library(name: "MyPackage", targets: ["MyPackage"])],
targets: [.target(name: "MyPackage", path: "Sample.swiftpm/Sources")]
)
パッケージのインポート
- 作成した
MyPackageをXcodeプロジェクトに組み込みます。
![]() |
![]() |
![]() |
- Playgrounds側の
ContentViewのアクセスレベルをpublicに変更します。
import SwiftUI
public struct ContentView: View {
public init() {}
public var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Playgrounds")
}
}
}
- Xcode側の
ContentView.swiftを削除します。
Sample/
├── Sample.xcworkspace
├── Sample.xcodeproj
├── Sample/
│ └── SampleApp.swift
└── MyPackage/
├── Package.swift
└── Sample.swiftpm
├── Package.swift
├── MyApp.swift
└── Sources/
└── ContentView.swift
- Xcode側の
SampleAppを修正し、MyPackageをインポートします。
import SwiftUI
import MyPackage
@main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
- Xcodeプロジェクトを実行して
Playgroundsが表示されることを確認します。
運用方法
開発時
普段の開発時 つまり Swiftを書く場合は Sample.swiftpm を開いて作業します。
Xcode、Playgroundsどちらで開いても問題ありません。
設定変更時
プロジェクトの設定を変更したい場合は Sample.xcworkspace を開いて作業します。
- Info.plistの設定
- Run Scriptの設定
- Xcode Cloudの設定
などがXcodeのGUI上で出来るようになります。
注意点
App.swift と プロジェクトの設定ファイル がそれぞれ2つずつ存在する形になります。
その点に注意して運用する必要があります。
App.swift
出来る限りAppでは 何もせずにContentViewを呼び出すだけ の方針にすることをオススメします。
何かの処理が必要な場合もXcode, Playgroundsで同じ形になるようにしましょう。
プロジェクトの設定ファイル
Xcode CloudでArchiveする 方針にすることをオススメします。
PlaygroundsでArchiveすると、Xcode側で設定した内容が反映されない形でリリースすることになります。
今回の構成を活かすためには Xcode か Xcode Cloud でのArchiveが必要です。
iPadでの開発を叶えるためには Xcode Cloud 一択になりますよね!
補足
こだわる人向けに
iPadでの開発、というニッチな領域を攻めている人はこだわりたい部分も多いと思います。
何か役に立つかもしれないので、私が気づいたことをスクラップレベルで記載しておきます。
パッケージ関連の名前
Swift Packageやマルチモジュール化の話にはなりますが、Package.swift で設定する名前について記載します。
MyPackageA, B, Cはそれぞれ別の名前でも問題ありません。
MyPackageCについては products , targets で同じ名前に揃える必要があります。
またXcodeプロジェクト側のターゲット名 Sample と同じにすることはできません。
ここで設定した名前に応じて import MyPackageC のようにインポートすることになります。
let package = Package(
name: "MyPackageA",
platforms: [.iOS(.v17)],
products: [.library(name: "MyPackageB", targets: ["MyPackageC"])],
targets: [.target(name: "MyPackageC", path: "Sample.swiftpm/Sources")]
)
Swiftコード用のディレクトリ
Sample.swiftpm 配下に作成する Sources ディレクトリは任意の名前で問題ありません。
ディレクトリ構成
ルートディレクトリを綺麗に保ちたいという考えがあると思います。
特にPlaygroundsプロジェクト Sample.swiftpm はルートにあった方が都合が良いでしょう。
一例として以下のようなディレクトリ構成はどうでしょうか。
Sample/
├── Package.swift
├── Sample.xcworkspace
├── Sample.swiftpm
│ ├── Package.swift
│ ├── MyApp.swift
│ └── Sources/
│ └── ContentView.swift
└── Sample/
├── Sample.xcodeproj
└── Sample
└── SampleApp.swift
ルートの Sample/ 自体を MyPackage/ の代わりに利用する形です。
併せて、あまり利用しない Sample.xcodeproj を Sample/Sample/ 配下に移動しています。
これを実現するには Sample.xcworkspace/contents.xcworkspacedata を下記のように修正します。
ルートを指定したい場合は空文字 location = "group:" と記述することで実現することができます。
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:">
</FileRef>
<FileRef
location = "group:Sample/Sample.xcodeproj">
</FileRef>
</Workspace>
おわりに
Playgroundsプロジェクトのコードをパッケージ化し、Xcodeプロジェクトから利用する構成
を紹介しました。
詳細なプロジェクト設定が必要な場合、iPadのみで開発を完結させることはまだ難しい面があります。
ただし、設定のみをMacで行い、日々の開発をiPadで行うことは十分に可能です。
この構成を活かして、実務レベルのプロジェクトにもiPadでの開発体験を持ち込んでみてはいかがでしょうか?
WWDC24で Swift Playgrounds に更なるアップデートがあることを期待しています!














Discussion