$ cat blog/

$ cat blog/entry_004.md

Swift という名の新しいコンパイラ
— アセンブラ世代がiOSに挑む

A New Compiler Called Swift
— An Assembly-Era Engineer Takes On iOS

Swift라는 이름의 새로운 컴파일러
— 어셈블러 세대가 iOS에 도전하다

2026-04-02 entry_004 ./mobile/

LDR R0, =XCODE — 新しいコンパイラとの出会い

2025年の秋、kitemir.jp のバーチャル試着機能をiPhoneアプリとして提供することを決めた。

理由は単純だった。スマートフォンで試着できれば、より多くの人に届けられる。外出先で気に入った服を、その場で仮想試着できれば、購買体験がまったく変わる。それだけで、作る価値がある。

Xcodeをインストールした日、最初の感想は「でかい」だった。汎用コンピュータのOSを初めて見たときと同じ感覚だ。画面いっぱいに並んだメニュー、ウィンドウ、パネル。どこから手をつければいいのかわからない。

だが、あのときも最終的にはマスターした。今回もそうするしかない。

$ xcode-select --install
$ swift --version
// Apple Swift version 5.10
// Target: arm64-apple-macosx14.0
//
// 40年ぶりの、新しいコンパイラだ。

Swift Optionals — 40年分のNull Pointer地獄を終わらせる型システム

アセンブラの世界では、ポインタのNULLチェックは「開発者の責任」だった。常に。例外なく。

; NULLチェック(アセンブラ版)
MOV AX, [ptr]      ; ポインタの値をロード
CMP AX, 0          ; NULLかどうか判定
JE  null_error     ; NULLならエラーハンドラへ
; ──────────────────────────────────────────
; 忘れたら? → セグメンテーション違反
; 実行時まで、誰も教えてくれない。

C言語に移行したあとも、NULL参照バグは永遠のテーマだった。コードレビューでNULLチェック漏れを探す作業を、何千回やってきたかわからない。

Swiftに Optional<T> という概念があることを知ったとき、最初は「なんて冗長な」と思った。

// nilかもしれない値はOptional型で宣言する
var userImage: UIImage? = nil  // ?がOptionalを示す

// 使う前に必ずアンラップしなければならない
if let image = userImage {
    // ここでは image が確実に存在する
    displayImage(image)
} else {
    showPlaceholder()
}

// Optional無視して直接使おうとすると?
let w = userImage.size.width
// → Compile error:
//   Value of optional type 'UIImage?'
//   must be unwrapped first
// → 実行前に、コンパイル時点で止まる。

40年間、人間の注意力に任せていたことを、型システムが保証する。忘れても、誰かが止めてくれる。しかも実行前に。

> INSIGHT: Optional<T> is not overhead.
> It's 40 years of NULL pointer bugs,
> eradicated at compile time.
>
> // コンパイラが、コードレビュアーになった。

SwiftUI の宣言的UI — 「どうするか」ではなく「どうあるべきか」

アセンブラは究極の命令型言語だ。CPUに「次にこれをやれ」と1命令ずつ指示する。画面にピクセルを描くときも、座標を計算して、色を設定して、バッファに書き込む——という手順をすべて明示する。

SwiftUIは、発想がまったく逆だった。

// 「この画面はこうあるべき」と宣言するだけ
struct HomeView: View {
    @State var products: [Garment] = []

    var body: some View {
        VStack {
            Text("バーチャル試着")
                .font(.title)
                .foregroundColor(.purple)

            LazyVGrid(columns: columns) {
                ForEach(products) { product in
                    ProductCard(garment: product)
                }
            }
        }
    }
}
// 「どう描くか」を命令しない。
// 「何を表示すべきか」だけを宣言する。
// @Stateが変われば、UIは自動で更新される。

最初は気持ち悪かった。「画面を描け」という命令を書かずに、画面が描かれる。状態(@State)が変われば、UIが勝手に動く。

だが、ある瞬間に気づいた。

これは状態機械だ。

制御プログラムで書いていた「状態遷移図」と同じ構造だ。@State は現在の状態レジスタ。body は「この状態のとき、出力はこうあるべき」というルール。状態が変われば遷移が発生し、出力(UI)が更新される。

> SwiftUI is a state machine.
> @State = current state register
> body = output function
> view update = state transition
>
> // 1985年に書いた状態遷移図が、
> // iPhoneの上で動いている。

.pbxproj の罠 — 40年前のリンカースクリプトと同じ顔をしていた

開発中に最もはまった落とし穴が、Xcodeのプロジェクトファイル(.pbxproj)だった。

アセンブラやCの開発では、ソースファイルを追加したらMakefileに明示的に書かなければならなかった。リンカースクリプトにも登録が必要だった。「ファイルが存在する」と「ビルドに含まれる」は、常に別の話だ。

Swiftでも同じだった。Finderで .swift ファイルをフォルダに追加しても、Xcodeのプロジェクトには認識されない。.pbxproj ファイルに4か所、手動で追記しなければならない。

// .pbxproj の内部(一部抜粋)

/* PBXBuildFile section */
ABC123 /* NewView.swift in Sources */ = {
    isa = PBXBuildFile;
    fileRef = DEF456 /* NewView.swift */;
};

/* PBXFileReference section */
DEF456 /* NewView.swift */ = {
    isa = PBXFileReference;
    lastKnownFileType = sourcecode.swift;
    path = NewView.swift;
    sourceTree = "<group>";
};

// + グループセクションにも追記
// + Sourcesビルドフェーズにも追記
// ──────────────────────────────────────
// 合計4か所。1か所でも漏れると……

エラーメッセージはひどかった。「Cannot find type 'NewView' in scope.」ファイルは存在する。コードも正しい。なぜビルドできない?

3時間後に原因がわかったとき、思い出したのは40年前のことだった。

リンカースクリプトのOBJECTS一覧にファイルを追加し忘れて、「symbol not found」で詰まった新人エンジニア時代。あの頃と同じミスを、68歳でまたやっていた。

ld: symbol not found(1980年代)
Cannot find type in scope(2025年)

40年経っても、ビルドシステムは言ってくる。「ちゃんと登録しろ」と。

App Store への道 — デバイスドライバ認定と同じ匂いがした

デバイスドライバを出荷するには、認定が必要だった。仕様に適合しているか、検査機関がテストする。通過して初めて、製品に組み込める。当時は当たり前のプロセスだった。

App Storeへの申請は、似ていて違った。Apple Developer Programへの登録。Bundle IDの設定。Certificates、Identifiers、Profiles。署名用の秘密鍵の管理。プロビジョニングプロファイルの設定。

だが本質は同じだ。「信頼できるコードだと証明してから、出荷する」。

初めてのビルドが実機で動いたとき——SplashViewがiPhoneの画面に表示されたとき——あの頃と同じ達成感があった。

@main
struct KitemirVTOApp: App {
    var body: some Scene {
        WindowGroup {
            SplashView()
            // ↑ これが実機に表示されたとき、
            //   涙が出そうになった。
        }
    }
}
> // 実機で初めて動いた瞬間:
> Build Succeeded
> Installing "KitemirVTO"...
> Launched
>
> // 68歳。アセンブラから始まって、
> // iOSアプリを動かした男。
$ swift build --target KitemirVTO
Build complete! (47 warnings, 0 errors)
// warning 47個。全部潰した。
// エラー0。
// 次はApp Store審査だ。