iOSアプリのプロジェクト作成 / XIB を使った開発
環境構築はじめに
カレーのレシピが1つではないように、iOSアプリ開発の方法も1つではありません。
今回紹介する方法は、私が行っている標準的な方法ですが、ベストとは限りません。プロジェクトや人それぞれにベストがあります。
ここで紹介する私のレシピが、あなたにあった味であれば幸いです。
実行環境
⛏ Xcode 12.5
🕊 Swift 5.3.2
🍎 macOS BigSur 11.1
📱 iOS 13.0 ~ 14.5
サンプルについて
サンプルはDoshCook/SwiftRecipesSampleにあります。
プロジェクトを作成しよう
プロジェクトを作成しましょう。
iOS App を選択して next を押します。(図1)

図1
Product Name をつけて、以下のように設定をします。(図2)
| 項目 | 選択項目 |
|---|---|
| Interface | Storyboard |
| Life Cycle | UIKit App Delegate |
| Language | Swift |

図2
ディレクトリ整理をしよう
初めからあるファイルの整理をしておきましょう。
プロジェクト作成時の構成は以下のようになっています。
.
├─ プロジェクト名.xcodeproj
└─ プロジェクト名
├── AppDelegate.swift # アプリ全体のライフサイクルを管理する(必須)
├── Assets.xcassets # 画像や色を管理する
├── Base.lproj # LaunchScreen.storyboard と Main.storyboard がはいっている
├── Info.plist # アプリ実行時に必要な情報を設定するファイル(必須)
├── SceneDelegate.swift # Scene を管理する
└── ViewController.swift # デフォルトで用意されたViewController
Resources と Scripts と Storyboard で分割します。
.
├─ プロジェクト名.xcodeproj
└─ プロジェクト名
├── Scripts
| ├── AppDelegate.swift
| ├── SceneDelegate.swift
| └── ViewController.swift
├── Resources
| ├── Info.plist
| └── Assets.xcassets
└── Sotryboard
└── Base.lproj
Xcode内のプロジェクトツリーと、実際のディレクトリ内の構成が図3, 図4のように一致するようにしてください。

図3

図4
Info.plist の場所を変更したため、以下のようなエラーが出てビルドができなくなります。
Build input file cannot be found: '/Users/uruly/DoshCook/XcodeProjects/SwiftRecipesSample/SwiftRecipesSample/Info.plist'
Build Settings で Info.plist を Resources 下になるように修正します。(図5)

図5
本サイトでは基本的にこの構成でフォルダ分けを行っています。
SceneDelegate について
iOS13から SceneDelegate.swift というファイルが、テンプレートとしてプロジェクトに生成されるようになりました。以下の図6のように、同じアプリを分割して同時に開く場合に利用されます。
SceneDelegate を使わない場合
iOS13以下への対応をしたい場合や、複数画面を起動して欲しくない場合には以下の手順でファイルを削除しましょう。
-
Info.plist より
Application Scene Manifestを削除する

図7 -
AppDelegate.swiftよりUISceneSession Lifecycle以下を削除するAppDelegate.swift から取り除きます。
// 以下を削除する // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } -
AppDelegate.swiftにvar window: UIWindow?を追加するimport UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { // 追加する var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } } -
SceneDelegate.swift を削除する
最後にSceneDelegate.swiftを削除することでSceneDelegate.swiftを削除することができます。
xib を使った開発
iOSアプリを開発する方法は、Swift言語を用いたものだけでも多岐にわたります。UserInterface(UI)の実装方法についても同様で、Storyboard / xib / SwiftUI といったInterface Builder (IB) を用いた方法とコードだけで書く方法があります。
- Storyboard
- xib
- SwiftUI
- UIKit のコードのみ
xib を使う理由
それぞれの方法にメリット・デメリットはありますが今回は Storyboard と xib の比較のみを行います。
Storyboard と xib はどちらもIBを使った方法です。Storyboardは画面遷移が明確になるメリットがありますが、チーム開発時のコンフリクトや、イニシャライザでの依存性注入 (DI)が難しいなどのデメリットにより、 xibを用いた開発を選択します。
| チーム開発 | イニシャライザでのDI | |
|---|---|---|
| xib | ○ | ○ |
| Storyboard | △ | △ |
イニシャライザでのDIとは
依存性の注入 (Dependency Injection)をイニシャライザで行うことです。
xib を用いる場合には、以下のようにイニシャライザで値を渡すことができます。
import UIKit
final class ViewController: UIViewController {
private let initialText: String
// イニシャライザで値を渡す
init(initialText: String) {
self.initialText = initialText
super.init(nibName: "ViewController", bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 生成時
let viewController(text: "イニシャライザで渡したい値")
Storyboard の場合は、iOS13からイニシャライザでのDIができるようになりました。
class ViewController: UIViewController {
private let initialText: String
init?(coder: NSCoder, initialText: String) {
self.initialText = initialText
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 生成時
let storyboard = UIStoryboard(name: "ViewController", bundle: nil)
let viewController = storyboard.instantiateInitialViewController { coder in
ViewController(coder: coder, initialText: "イニシャライザで渡したい値")
}
iOS13ではStoryboardでもDIができる件について - Qiita より引用
これらは組み合わせて使うこともできます。参画するプロジェクトによってやり方は様々です。一概に良し悪しは決められませんが、本サイトでは主に xib を用いた方法で実装していきます。
画面を作成する
新規に Scripts 以下に Views フォルダを作成します。
ViewController の名前は CreateProjectSampleViewController という名前にする予定なので、CreateProjectSample というフォルダを作成しました。
現在の構成は次の通りです。
.
├─ プロジェクト名.xcodeproj
└─ プロジェクト名
├── Scripts
| ├── AppDelegate.swift
| ├── SceneDelegate.swift
| └── Views
| └── CreateProjectSample
| ├── CreateProjectSampleViewController.swift
| └── CreateProjectSampleViewController.xib
├── Resources
| ├── Info.plist
| └── Assets.xcassets
└── Sotryboard
└── Base.lproj
New > File より Cocoa Touch Class を選択して Next を選択します。(図8)

図8
CreateProjectSampleViewController という名前で、Also create XIB file にチェックを入れて作成します。(図9)
| 項目 | 入力項目 |
|---|---|
| Class | CreateProjectSampleViewController |
| Subclass of | UIViewController |
| Also create XIB file | チェックを入れる |
| Language | Swift |

図9
最初の画面の表示
Storyboard を使う場合は、Storyboard 上の矢印(→)が指す画面が、アプリ起動後に開かれます。 xib を使った開発では、起動時に開く画面をコードで示してあげる必要があります。
-
Main.storyboardは用いないので削除します。 -
Info.plistのMain storyboard file base nameを削除します。

図10 -
アプリ起動時に開く画面の設定をコード上で行う
起動画面の指定をする場所は、先述のSceneDelegate.swiftを用いるかどうかによって異なります。-
SceneDelegate.swiftを用いる場合
SceneDelegateを用いる場合には、Info.plist>Application Scene Manifest>Scene Configuration>Application Session Role>Item 0(Default Configuration)内のStoryboard Nameを削除します。

図11次に
SceneDelegate.swiftのscene(_:willConnectTo:options:)内に以下を書きます。import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } // 起動後に表示画面を指定する // windowの生成 let window = UIWindow(windowScene: windowScene) // 初回起動時に開きたい ViewController を指定 let viewController = CreateProjectSampleViewController(nibName: "CreateProjectSampleViewController", bundle: nil) // windowのルートに設定 window.rootViewController = viewController // window を表示する window.makeKeyAndVisible() // self.window を生成した window にする self.window = window } } -
SceneDelegate.swiftを用いない場合
SceneDelegateを用いない場合には、AppDelegate.swiftのapplication(_:didFinishLaunchingWithOptions)内に以下を書きます。@main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 起動後に表示画面を指定する // windowの生成 let window = UIWindow(frame: UIScreen.main.bounds) // 初回起動時に開きたい ViewController を指定 let viewController = CreateProjectSampleViewController(nibName: "CreateProjectSampleViewController", bundle: nil) // windowのルートに設定 window.rootViewController = viewController // window を表示する window.makeKeyAndVisible() // self.window を生成した window にする self.window = window return true } }
-
これで xib を使った開発をする準備ができました。
あとは、Views 配下に ViewController を配置していく形で開発を進めていきます。
おわりに
このサイトで紹介するレシピの多くはこのプロジェクト作成方法をベースとしていく予定です。
