モバイルアプリ開発で身に付けておきたい知識
iOS と Android について

Android📱と Apple iOS📱は、色々なランキングによって誤差はありますが、王道のWindowsを抜いてシェア率が1位と2位になっています。2025年世界で最もユーザーが触れるOSとなっています。
iPhone が大人気だった日本ではありましたが、近頃 Samsung や Google Pixel を主軸に、iPhone よりスペックや性能が高くなってきており、カスタマイズ性も強く、周りの友達もどんどんAndroidユーザーが増えていました。
Android とは
Googleが開発した、スマートフォンやタブレット向けのOSです。
Androidはオープンソースで、安い端末から超ハイスペックな端末まで、予算に合わせて選べることができます。カスタマイズ性が高く、比較的審査が早い「Google Play」からアプリをダウンロードすることができます。
Kotlin がネイティブ言語として使われます。
Apple iOS とは
Apple社がiPhoneやiPadなどの自社製モバイル機器向けに開発したOSです。
シンプルで直感的なインターフェースが特徴で、高い安定性やセキュリティ、Apple製品間の連携の良さがあり、アプリは厳しい審査を通ってきた「App Store」からのみダウンロードできる点がメリットです。
Swift がネイティブ言語として使われます。
Swift と Kotlin の違いと似てるところ
Swift と Kotlin は、それぞれ Apple と Google が「モダンな開発体験」を目指して同時期に進化させてきた言語です。そのため「そっくりだけど、プラットフォームが違う」という関係性にあります。
言語仕様の共通点
両言語とも、Objective-c や Java といった先代言語の不満点を解消するために設計されているため、構文の哲学は非常に近いです。
Null 安全 (Null Safety)
Nullを上手に扱うための仕組みのことです。
安全にNullを扱うために、OptionalやNullableというNullを許容する型を使っています。
- Swift
- String?(Optional)
- Kotlin
- String?(Nullable)
Nullを許容する型(オプショナル)は、?または!で表します。
型推論
変数宣言時に型(int, string, boolなど)を省略しても、コンパイラが「文脈(初期値や使用方法)」から型を導出します。
関数型プログラミング
イミュータブル(不変)な変数の宣言
- 不変 (Read-only)
- Swift → let
- Kotlin→ val
- 可変 (Mutable) :SwiftとKotlin → var
「関数を引数として渡す」高階関数
- Swift(最後の引数がクロージャの場合、関数の外に出して書く Trailing Closure 構文)
- 匿名引数を使うため、非常に短く書ける。(
$0, $1)
// 簡略記法($0 は第1引数を指す)
let simplified = numbers.map { $0 * 2 }- Kotlin(同様の構文を持ち、引数が 1 つの場合は暗黙の変数名 it を使う)
- 単一のキーワードを使い、可読性を重視する。(変数名 it)
// 簡略記法(it は第1引数を指す)
val simplified = numbers.map { it * 2 }関数型らしい「列挙型」
↪️定数の集合をひとまとめにし、名前付きで定義できるデータ型
- Swift → Enum 自体が拡張されている
- データの持ち方:case success(user: User)
- 網羅性チェック:switch
- 向いていること:軽量な状態管理、値の分類
struct User { let name: String }
enum NetworkState {
case loading // 読み込み中(データなし)
case success(user: User) // 成功(Userデータが紐付いている)
case failure(error: String) // 失敗(エラーメッセージが紐付いている)
}
// 使い方
let currentState = NetworkState.success(user: User(name: "田中"))
switch currentState {
case .loading:
print("くるくる回るアイコンを表示")
case .success(let user):
print("\(user.name)さんのプロフィールを表示")
case .failure(let message):
print("エラー表示: \(message)")
}- Kotlin → クラスの継承を利用している
- データの持ち方:data class Success(val user: User)
- 網羅性チェック:when
- 向いていること:複雑なデータの保持、階層構造
data class User(val name: String)
sealed class NetworkState {
object Loading : NetworkState()
data class Success(val user: User) : NetworkState()
data class Failure(val error: String) : NetworkState()
}
// 使い方
val currentState: NetworkState = NetworkState.Failure("接続タイムアウト")
val message = when (currentState) {
is NetworkState.Loading -> "読み込み中..."
is NetworkState.Success -> "${currentState.user.name}さん、こんにちは!"
is NetworkState.Failure -> "問題発生: ${currentState.error}"
}モダンな構文
セミコロンが不要、型を後ろに書くスタイル(name: String)などが共通しています。
技術的な決定差:メモリ管理とランタイム
Swift
- コンパイル先
- ネイティブコード (LLVM経由)
- LLVMで動くことのメリットはOSとの親和性が極めて高いこと
- メモリ管理
- ARC (Automatic Reference Counting)
- 実行環境
- ランタイムが非常に軽量
Kotlin
- コンパイル先
- JVM Bytecode (通常) / Native
- メモリ管理
- ガベージコレクション
- 実行環境
- JVM (Java Virtual Machine) 上で動作
- JVMで動くことのメリットは Javaの豊富なライブラリが使える
ARC vs GC(メモリ管理)
Swift (ARC)
- コンパイル時に「どこでメモリを解放するか」のコードを自動挿入します。
- 実行時の余計な負担が少なく、メモリ解放のタイミングが予測可能ですが、循環参照(Strong Reference Cycle)を開発者が意識する必要があります。

Kotlin (GC)
- 実行時にバックグラウンドで不要なメモリをスキャンして解放します。
- 開発者は参照関係をあまり気にせず書けますが、GC実行時に一瞬動作が止まる(Stop The World)のリスクがあります。
メモリ解放のタイミングの違い
- ARC
- 「使い終わった瞬間にゴミ箱に捨てる(即時)」
- GC
- 「ゴミが溜まってきたら、掃除屋さんがまとめて回収に来る(非同期)」
値型 (Value Types) の扱い
データの持ち方についても、Swift の方がより「低レイヤー」に近い厳格さを持っています。
Swift: Struct(値型)
- データの安全性
- コピーが渡されるため、予期せぬ書き換えを防げます。
- 実行速度
- メモリのスタック領域に配置されることが多く、非常に高速です。
Kotlin: Class(参照型)
- データの安全性
- 参照が渡されるため、複数の場所から同じデータを書き換えられてしまうリスクがあります。安全に扱うために、不変(val)にしたり、コピーを作成するメソッドを活用する。
- 実行速度
- メモリのヒープ領域に配置され、実行時にガベージコレクションがメモリを管理します。
関数型と並行処理
列挙型
- Swift
- Associated Values(動的で柔軟データ保持)を持てるため、列挙型の中に具体的な値を持たせることができ、値の取得にはswitch文などの強力なパターンマッチングが可能です。
- Kotlin
- Sealed Class(継承できるサブクラスを制限) は、 Swift の Enum に近い挙動を実現します。when文で網羅性のチェックが可能になり、安全で保守性の高いコードを書けます。
並行処理
- Swift
- async/await に加え、アクターモデル(独立した軽量プロセスが、情報を非同期に送り合うことで、並行処理を実現する)を言語レベルで導入し、データ競合をコンパイルタイムで防ぐ仕組みを強化しています。
- Kotlin
- Coroutine (スレッドを一時停止せずに非同期処理や並行処理を実現) が強力です。軽量スレッドのような扱いで、非同期処理を同期処理のように書くスタイルが非常に洗練されています。
開発体験 (DX) の違い
エコシステム
- Swift
- Apple エコシステムに特化。SwiftUI による宣言的 UI が強力ですが、Xcode というツールに縛られがちです。
- Kotlin
- Android だけでなく、サーバーサイドや、Kotlin Multiplatform による iOS/Android のロジック共有が活発です。
ビルド速度
- Swift
- LLVM による最適化が重く、大規模プロジェクトではビルド時間が課題になりやすいです。
- Kotlin
- JVM の中では遅い方ですが、インクリメンタルビルドの効率が良い傾向にあります。
Swift UI と Jetpack Compose(宣言的UI)
vs
UIKit と Android View(命令的UI)
SwiftUI / Jetpack Compose (宣言的UI)

開発効率と保守性
ReactのようなUIの部品を当てはめるように可読性が高く直感的に書きやすいです。
メリット
- 圧倒的な開発スピード
- UIとロジックを一つの言語で完結できるため、記述量が劇的に減ります。
- 状態とUIの同期
- データ(State)が1箇所変われば、関連するUIが自動で同期されます。
- プレビュー機能
- 実機やシミュレータを起動しなくても、コードの変更を即座に確認できます。
デメリット
- オーバーヘッド
- データが変わるたびに「画面のどの部分に差分があるか」を計算する処理が走ります。
- ブラックボックス化
- フレームワーク側が賢く動く分、意図しないタイミングで再描画が起きた際のデバッグや、極限のパフォーマンスチューニングが難しい場合があります。
Reactのような宣言的UIは、状態管理(State)と表示が直結しているため、バグが入り込みにくく、コード量も圧倒的に少なく済みます。
UIのプロトタイピングも速いので、スピード感が求められる開発にはSwiftUIが適しています。
UIKit / Android View (命令的UI)

既存資産と深いカスタマイズ
1行1行とコードを順を追うように書きます。
メリット
- パフォーマンスの予測可能性
- 静的な画面であれば、一度配置を決めてしまえば再描画が走りません。
- 枯れた技術の安定性
- 10年以上の歴史があるため、OSの深い部分の挙動まで制御しやすく、トリッキーなアニメーションや複雑なスクロール制御は依然としてこちらが強力です。
- デバッグの容易さ
- 「このボタンを押したから、この命令が実行された」という実行順序が明確なため、複雑な状態変化によるバグを追いやすい側面があります。
デメリット
- コードの肥大化
- XML、レイアウトファイル、Viewの実装コードがバラバラになり、管理が煩雑です。
- 状態管理の複雑さ
- データが変わるたびに「どの部品を更新するか」を手動で書くため、更新漏れ(データは変わったのに画面が変わっていない)が起きやすいです。
UIKitには長年の蓄積があり、OSの深い部分の挙動を制御したり、非常に複雑なアニメーションを実装したりする際にはまだ一段階上です。
また、多くの企業では既存のUIKit資産があるため、それをSwiftUIと共存させるスキルも重要です。
Swift UI と UIKit の歴史

- UIKit時代(2008年〜)
- 「命令型」 「ボタンをここに置いて、赤色に変えて」と、手順をすべて指示するスタイル。細かく制御できるが、コードが長く複雑になりがち。
- Swift登場(2014年〜)
- 言語がObjective-CからSwiftへ進化。より安全で書きやすくなったが、UIの作り方はまだ「手順指示」のままだった。
- SwiftUI時代(2019年〜現在)
- 「宣言型」 「データが○なら、この画面を表示して」と、状態を定義するだけ。Reactのように直感的で、コード量が激減。
UIKitは手順を指示する「命令型」で長年主流でしたが、現在はデータとUIが直結した「宣言型」のSwiftUIへ移行しており、より速く安全に開発できるようになっています。
Class(参照型)とStruct(値型)について
Class(クラス):実体の参照
Classは参照型で、複数の場所から一つの実体を共有する仕組みです。継承が必要な場合に利用します。オブジェクト指向の王道です。
- 役割
- 共通の「実体」を管理する。
- 「あの」口座、「あの」ユーザー、「あの」プレイヤー。(たった一つのアイデンティティ(個体)をみんなで指差す)
- 特徴
- メモリ上の同じ場所を指し示すため、一箇所で変更すると全員に反映される。
- 継承(共通機能の引き継ぎ)ができる。
- 意味
- データベース接続やネットワーク管理など、アプリ全体で「たった一つ」の状態を共有したい場合に向いています。
// swift の場合
class User {
var name: String
// イニシャライザ(初期化)が必須
init(name: String) {
self.name = name
}
}
// Kotlin の場合
class User(var name: String)Struct(構造体):データの値
Structは値型で、データのコピーを渡す仕組みです。安全性とパフォーマンスに優れています。厳密にはデータ構造ですが、関数型プログラミングに近い性質を持ちます。(不変性や副作用を防ぐ)
- 役割
- 不変(Immutable)なデータを保持する。
- 「100円」、「青色」、「座標(x:10, y:20)」。(同じ中身なら、別々のコピーを持っていても構わない)
- 特徴
- スレッドセーフでコピーが渡されるため、予期せぬ場所でデータが書き換えられるリスクがない。
- スタック領域でメモリ管理が高速。
- SwiftUIでの意味
- SwiftUIのViewがStructなのは、状態が変わるたびに高速に作り直す必要があるため。
// Swift の場合
struct Location {
var x: Double
var y: Double
}
// Kotlin (Struct に相当するもの)
data class Location(val x: Double, val y: Double)インターフェースとプロトコル(OOPの一部)
インターフェースはオブジェクト指向における「概念」で、プロトコルはSwiftにおける「その具体的な実装」です。どちらも「そのクラスや構造体が何ができるか」という振る舞いのルールを定義するものです。
インターフェース
インターフェースとは、「振る舞いのルール」だけを定義したものです。
中身(どうやるか)は書かず、「何ができるか」という「名前だけを並べたリスト」です。 Swiftにおける「プロトコル」は、このインターフェースを実現するための機能です。
// java
// ルールの定義(インターフェース)
interface Chargeable {
void charge(); // 振る舞い の宣言
int getBatteryLevel(); // 状態の取得 の宣言
}
// ルールを実装するクラス(implements を使う)
class IPhone implements Chargeable {
private int batteryLevel = 50;
@Override
public void charge() {
System.out.println("iPhoneを充電中です...");
}
@Override
public int getBatteryLevel() {
return this.batteryLevel;
}
}
class Android implements Chargeable {
private int batteryLevel = 30;
@Override
public void charge() {
System.out.println("Androidを急速充電中です...");
}
@Override
public int getBatteryLevel() {
return this.batteryLevel;
}
}インターフェースはオブジェクト指向全般の概念ですが、Swiftではそれをプロトコルという名前で呼び、さらに強力な機能を持たせています。
プロトコル
プロトコルとは、型が実装すべきメソッドやプロパティの定義をまとめた「ルール」です。
これを使うことで、具体的な実装の詳細を隠し、部品同士の結びつきを弱く、疎結合にできるため、テストやメンテナンスがしやすいコードが書けます。
// ルールを定義(インターフェース)
protocol Chargeable {
var batteryLevel: Int { get set } //「何を持っているか」という 状態 の定義
func charge() // 「何ができるか」という 振る舞い の定義
}
// ルールを構造体で採用する
struct iPhone: Chargeable {
var batteryLevel: Int = 50
func charge() {
print("iPhoneをLightningで充電中...")
}
}
struct Android: Chargeable {
var batteryLevel: Int = 30
func charge() {
print("AndroidをUSB-Cで急速充電中...")
}
}インターフェース = プロトコル?
そうです。「考え方」としては全く同じです。
ただSwiftのプロトコルは、構造体(Struct)に適用できたり、extensionでデフォルト実装(メソッドの処理内容を事前に定義)を持てたりするため、一般的なインターフェースよりも柔軟で強力な設計ができるツールです。
Swiftでは Protocol-Oriented Programming (POP) という考え方が中心にあります。

アーキテクチャ
MVVM

Viewからビジネスロジックを分離し、ロジック部分だけの単体テストを書きやすくするためのアーキテクチャです。
- Model:
- データそのもの(DBやAPIのレスポンス)
- struct User というデータ構造
- サーバーからデータを取ってくる NetworkService
- View:
- 見た目やUI。ユーザーの操作を受け取り、ViewModelの状態を表示する。
- 「ボタンが押された」というアクションがあったことをViewModelに伝え、自分では計算しません。
- ViewModel: View専用のデータ提供者
- ViewModelは、ModelのデータをViewに表示しやすい形に変換し、Viewからのユーザー操作などのイベントを受けて必要なロジックを実行します。
- MVCのコントローラーは「処理の流れ」を管理するものでしたが、MVVMのビューモデルは「Viewが見せるべき状態」そのものを管理する。
- データの同期 (データバインディング)
- Viewの表示とViewModelの状態を同期させ、ViewModel内のデータが変われば、自動的に画面(View)の表示も更新される仕組み。

TCA

SwiftUIのために作られた、状態管理を一元化するためのモダンなアーキテクチャです。
- 仕組み: Redux(React)に近い考え方
- State、Action、Reducer、Storeという4つの要素で動きます。
- State(状態): アプリや画面のデータを保持する構造体。
- Action(操作): ユーザー操作やAPIレスポンスなど、画面で発生するイベント。
- Reducer(ロジック): 現状のStateとActionを受け取り、新しいStateへの変化と副作用(Effect)を返す関数。
- Store(ストア): State、Action、Reducerが実際に動作する場所。UIとロジックをつなぐ。
- 特徴
- アプリの状態が「一箇所」で管理されるため、「今アプリがどういう状態か」が予測しやすい。
(おそらく超)大規模なアプリ向けで、機能を小さく切り出して組み合わせやすく、テストが非常に書きやすい設計になっています。しかし、多機能であり学習コストがめちゃくちゃ高いです。

使い分け方法
MVVMは、シンプルで学習コストが低く、多くのプロジェクトで採用されている標準的な手法です。
TCAは、状態管理が厳格でテストのしやすさに特化しており、大規模で複雑なSwiftUIアプリに向いています。
モバイルアプリでは、基本的にMVVMを採用することが多いです。
始めるならiOSとAndroid、どちらを選ぶべきか?
- iOS (Swift)
- 厳格なメモリ管理(ARC)や値型(Struct)を活かした、「安全でリッチな体験」を突き詰めたい。
- Appleのエコシステムの中で、ハードウェアと密接に連携した開発を楽しみたい。
- Android (Kotlin)
- 高いカスタマイズ性と、サーバーサイドやクロスプラットフォーム(KMP)への「柔軟な拡張性」を重視したい。
- 多様なデバイスに対応する課題解決を楽しみたい。
まとめ
モバイルエンジニアを志す私が、SwiftとKotlinを開発する上で押さえておきたい概念をまとめました。
最近、モバイル用言語がオブジェクト指向と関数型が融合したと知ったばかりでしたが、新たにクラス(参照型)と構造体(値型)の違いや、インターフェースとプロトコルが振る舞いのルールを定義するもので、Swiftではそれをまとめてプロトコルとして扱うことを学べました。
MVCとMVVMが全く同じものだと勘違いしていたので、早めに気付けてよかったです。MVCは主にサーバーサイドで使われ、MVVMがアプリや複雑な画面に使われるようです。
ブログを書いて要点もまとまり、とても勉強になりました!
