iOSアプリをMultiProjects化させる手順

マルチモジュールに関する記事はネットで調べれば出てくるのですが、マルチプロジェクトの実装方法に関する記事がなかったので、今回書くことにしました。
誤り等があれば指摘していただけると幸いです。

# 手順

### Workspaceの作成
Xcodeを起動してからメニューの `File>New>Workspace...` もしくは `⌘+^+N` からWorkspaceを開きます。


### フォルダ作成
terminalもしくはFinderから.workspaceと同じ階層に`App`というフォルダを作成し、MultiProjectsフォルダの配下に`iOS`というフォルダを作成して下さい。

最終的に以下のフォルダ構成になっていればOKです。
![フォルダ構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/88df2757-e650-7669-f006-641838c78e68.png)

AppフォルダにはApp.swift, Assets.xcassetsなどを格納します。


### xcodeprojの追加

メニューの `File>New>Project...` もしくは `⌘+Shift+N` から`Staging`と`Production`というプロジェクトを新規作成し、.xcodeprojをAppフォルダの配下に追加します。
Add to と Group には [1. Workspaceの作成](https://qiita.com/tbskgm/items/0961bf0a49a35c8d35bb#workspace%E3%81%AE%E4%BD%9C%E6%88%90)で作成したWorkspaceを選択して下さい。

最終的には以下の画像のようになります。
![フォルダ構成（xcodeproj追加）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/146b7fbc-7a41-f9e1-f88f-296a2676ee40.png)


### .xcodeprojのファイル移動
今作成したStagingとProductionのファイルをiOSディレクトリの配下に移動させていきます。
ターミナルもしくはFinderを使用して移動させて下さい。
ここではターミナルを使用した方法を解説します。

```terminal:terminal
// pathには自身のパスを入れてください
cd path/MultiProjects/
mv Production/Production iOS/
// MultiProjectsの階層で行うこと
mv Production/Production.xcodeproj ../MultiProjects/
```

```terminal:terminal
// pathには自身のパスを入れてください
cd path/MultiProjects
mv Staging/Staging iOS/
// MultiProjectsの階層で行うこと
mv Staging/Staging.xcodeproj ../MultiProjects/
```

### App.swiftの共通化
App.swiftをどの環境（Staging, Production）からでも参照されるよう修正していきます。
StagingとProductionのどちらでも構いませんが、今回はProductionの方を使用します。
ContentView.swiftは[後ほど](https://qiita.com/tbskgm/items/0961bf0a49a35c8d35bb#xcodeproj%E3%81%ABmodule%E3%82%92%E5%8F%82%E7%85%A7%E3%81%97import%E3%81%99%E3%82%8B)使用するので、任意の場所に残しておいてください。
※StagingとProductionのApp.swiftとContentView.swiftは使用しないので削除します。
```terminal:terminal
// pathには自身のパスを入れてください
cd path/Production
mv ProductionApp.swift App.swift
mv Production/App.swift ../iOS
```

MultiProjectsの階層にあるStagingディレクトリとProductionディレクトリは必要ないので、削除してしまって大丈夫です。
```terminal:terminal
// MultiProjectsの階層で実行すること
rm -r Staging/
rm -r Production/
```

### workspaceDataを編集して、.xcodeprojが表示されるようにする

contents.xcworkspacedataを編集して、workspaceがファイルを参照できるようにしていきます。

```xml:contents.xcworkspacedata
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:App/Staging.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:App/Production.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:">
   </FileRef>
</Workspace>
```

### ファイルの参照設定

今のところこのような状態になっていると思うので、ファイルを参照できるようにしていきます。

![xcodeproj参照設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/dba0abc1-d1f9-cc6b-0d29-560f10102b09.png)

TARGETS > Build Phases > Compile Sources にApp.swiftを参照させます。
TARGETS > Build Phases > Copy Bundle Resources にはAssets.xcassetsとPreview Contentを参照させます。


### swift package initでpackage追加

MultiProjectsの配下にMultiModule化させるためのPackageを追加します。
// MultiProjectsの配下で行うこと
```terminal:terminal
swift package init
```
workspaceにpackage, Souces, Testsが追加されました。
![package追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/3a6d5802-60f3-6810-7222-84abc94c305c.png)


### AppディレクトリにPackage.swiftを追加

隠蔽したい階層にPackage.swiftを追加することで、存在を秘匿できるようになります。
今回の場合ですと、Appディレクトリ配下を隠します。

```swift:Package.swift
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "",
    products: [],
    targets: []
)
```
workspaceを確認すると、MultiProjectsの配下からAppディレクトリが表示されなくなっています！
![App配下秘匿.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/981cff2f-5a48-b348-632c-b949a9a29a2b.png)

### packageにmoduleを追加していく

MultiProjectsの配下にpackage.swiftを追加していきます。
こうすることでMulti Module化を進めていくことができます。
では、実装方法を見ていきましょう。
```
swift package init
```
上記コマンドを叩いた階層にPackage.swift, Sources, Testsが追加されています。

![MultiProjects配下にpackage追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/b2af07ca-bb98-aa92-4fdc-26a164add357.png)

### .xcodeprojにmoduleを参照し、importする

AppFeatureというフォルダにContentView.swiftを移管して、マルチモジュール対応を行っていこうと思います。
まずは不要なファイルを削除します。
SourcesとTestsの配下にあるファイルをすべて削除し、AppFeatureフォルダを作成してContentView.swiftを移動させます。

このままではエラーが発生するので、MultiProjects配下のPackage.swiftに修正を行います。
```diff_swift:package.swift
let package = Package(
    name: "MultiProjects",
+    platforms: [.iOS(.v13)], //SwiftUIはiOS13~でなければ使用できないため
    products: [
-        .library(
-            name: "MultiProjects",
-            targets: ["MultiProjects"]),
+        .library(name: "AppFeature", targets: ["AppFeature"])
    ],
    targets: [
-        .target(
-            name: "MultiProjects"),
-        .testTarget(
-            name: "MultiProjectsTests",
-            dependencies: ["MultiProjects"]),
+        .target(name: "AppFeature")
    ]
)
```

また、モジュール外からアクセスできるようにpublic修飾子を追加します。

```diff_swift:ContentView.swift
 import SwiftUI

-struct ContentView: View {
+public struct ContentView: View {
+    public init() {}

-    var body: some View {
+    public var body: some View {
         Text("Hello, world!")
             .padding()
     }
 }
```

StagingとProductionのそれぞれのTARGETS > Frameworks, Libraries, and Embedded ContentにAppFeatureを追加します。
こうすることでそれぞれのプロジェクトでフレームワークを参照できるようになり、importを行うことができるようになります。


### Manage schemeで名前を変更

現状はこのようになっています。
![scheme確認画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/ac55b8e0-9fcd-8ef1-98b0-f9b68f19be5c.png)

Scheme名　> Edit Scheme... > Manage Schemes...　> Schemeの列を任意の文字に修正すると以下の画像のようにできると思います。

![Scheme修正後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409860/68342e5d-7cde-7726-308b-ecd9e78fe763.png)


# 参考

https://date.notion.site/Swift-PM-Build-Configuration-4f14ceac795a4338a5a44748adfeaa40

https://zenn.dev/kalupas226/articles/73118709e316ad#app-%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AB%E5%85%A8%E3%81%A6%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%81%BE%E3%81%A8%E3%82%81%E3%82%8B%EF%BC%88%E4%BB%BB%E6%84%8F%EF%BC%89

https://tech.timee.co.jp/entry/2021/08/24/160205

https://note.com/wakinchan/n/neb23254e16b3

https://www.notion.so/isowords-8f8982eb3a9a4665b2fa688b06791b70

https://www.youtube.com/watch?v=AfCGqH4tLV4&t

https://www.youtube.com/watch?v=e_T1-XrYf4A&list=PPSV