🍬

VS Code でiOSアプリ開発 〜SweetPad 拡張機能

に公開

もうVS Codeなしでは生きていけない

ここしばらく VS Code で開発していて、さて、と Xcode に戻ろうとしたら、戻れなくなってしまった。 VS Code を使いたい。どうしても Xcode でなきゃという場面以外は。

結論

https://sweetpad.hyzyla.dev

SweetPad 拡張機能を利用することにした。

VS Code だけでなく、Cursorでも使えるとのこと。

やりたいこと

Xcodeで作ったプロジェクトをVS Codeで開き、開発する

環境

以下の環境で開発している

  • Mac: MacbookPro 2021 (Apple M1 Pro)
  • macOS: Sequoia 15.2
  • Xcode: 16.1
  • Visual Studio Code: 1.95.3 (Universal)

やる前の状況

  • Xcodeで iOS アプリのプロジェクトを新規作成
  • そのプロジェクトを VS Code で開く
  • 参照しているクラスなどが解決されず、コード補完が使えない

これを解決しようとして調べたところ、xcode-build-serverというツールがXcodeのビルド情報を提供(BSPをサポート)すると判明した。インストール・設定してみると上記の参照エラーが消え、コードが書けるようになった。

SweetPadとの出会い

もう少し調べると、xcode-build-serverを利用したVS Codeの拡張機能に行き当たった。SweetPadというのがそれだ。曰く

Build iOS/Swift apps using Visual Studio Code

コード補完、ビルド&実行、Swiftコードのフォーマット、シミュレータや実機での実行、CodeLLDBを利用したデバッグ、テスト、それらに必要なツールのインストールを支援してくれるとのこと。親切。

SweetPad拡張機能があれこれ面倒をみてくれる

ではインストールしてみよう。ちょっとしたインストールもSweetPad拡張機能が面倒をみてくれるので迷うことがない。

Xcodeでプロジェクトを作成しておく

まずはXcodeで新規プロジェクトを作成する。私と同じように困ってみること(VS Codeで開いてあーあの気持ちになる)。これは使えませんね、というのを確認する。しましたか? そうしたら VS Code に戻る。次のインストール手順を実行する。

最低限のインストール手順

  1. SweetPad拡張機能と、Swift拡張機能をインストールする。
  2. アクティビティバーでSweetPadを選択する。
  3. Toolsビューから必要なツールをインストールする。Homebrewxcode-build-serverは必須
  4. コマンドパレットから SweetPad: Generate Build Server Config を選択する。初回はスキーマの選択肢が出る。選ぶ。


    プロジェクトルートにbuildServer.jsonが生成される。

ここまでやれば、コードを書き始められる。インストール前に出ていた参照エラーが解決されたのがわかる。

フォーマッタ設定

prettierはswiftをサポートしていないので、フォーマッタもSweetPadが面倒を見てくれる。

Apple 公式が出している swift-format フォーマッタコマンドを利用しているので、まずそれをToolsビューからインストールする。

次に.vscode/settings.jsonに下記のように設定する:

{
 "[swift]": {
 "editor.defaultFormatter": "sweetpad.sweetpad",
 "editor.formatOnSave": true,
 },
}

…と書いたが、実は手元でこのformatOnSaveがうまく動いていない。

defaultFormatter の設定は効いていて、⇧⌥F(Format Document)のフォーマットは問題なく動くので、そちらを利用している。

フォーマットを設定したい時は、プロジェクトルートに .swift-format を置く。swift-formatコマンドが現在の設定を書き出してくれるので、それを編集して使うといいだろう。

 % swift-format dump-configuration > .swift-format
書き出される .swift-format の例
.swift-format
{
  "fileScopedDeclarationPrivacy": {
    "accessLevel": "private"
  },
  "indentConditionalCompilationBlocks": true,
  "indentSwitchCaseLabels": false,
  "indentation": {
    "spaces": 2
  },
  "lineBreakAroundMultilineExpressionChainComponents": false,
  "lineBreakBeforeControlFlowKeywords": false,
  "lineBreakBeforeEachArgument": false,
  "lineBreakBeforeEachGenericRequirement": false,
  "lineLength": 100,
  "maximumBlankLines": 1,
  "multiElementCollectionTrailingCommas": true,
  "noAssignmentInExpressions": {
    "allowedFunctions": [
      "XCTAssertNoThrow"
    ]
  },
  "prioritizeKeepingFunctionOutputTogether": false,
  "respectsExistingLineBreaks": true,
  "rules": {
    "AllPublicDeclarationsHaveDocumentation": false,
    "AlwaysUseLiteralForEmptyCollectionInit": false,
    "AlwaysUseLowerCamelCase": true,
    "AmbiguousTrailingClosureOverload": true,
    "BeginDocumentationCommentWithOneLineSummary": false,
    "DoNotUseSemicolons": true,
    "DontRepeatTypeInStaticProperties": true,
    "FileScopedDeclarationPrivacy": true,
    "FullyIndirectEnum": true,
    "GroupNumericLiterals": true,
    "IdentifiersMustBeASCII": true,
    "NeverForceUnwrap": false,
    "NeverUseForceTry": false,
    "NeverUseImplicitlyUnwrappedOptionals": false,
    "NoAccessLevelOnExtensionDeclaration": true,
    "NoAssignmentInExpressions": true,
    "NoBlockComments": true,
    "NoCasesWithOnlyFallthrough": true,
    "NoEmptyTrailingClosureParentheses": true,
    "NoLabelsInCasePatterns": true,
    "NoLeadingUnderscores": false,
    "NoParensAroundConditions": true,
    "NoPlaygroundLiterals": true,
    "NoVoidReturnOnFunctionSignature": true,
    "OmitExplicitReturns": false,
    "OneCasePerLine": true,
    "OneVariableDeclarationPerLine": true,
    "OnlyOneTrailingClosureArgument": true,
    "OrderedImports": true,
    "ReplaceForEachWithForLoop": true,
    "ReturnVoidInsteadOfEmptyTuple": true,
    "TypeNamesShouldBeCapitalized": true,
    "UseEarlyExits": false,
    "UseExplicitNilCheckInConditions": true,
    "UseLetInEveryBoundCaseVariable": true,
    "UseShorthandTypeNames": true,
    "UseSingleLinePropertyGetter": true,
    "UseSynthesizedInitializer": true,
    "UseTripleSlashForDocumentationComments": true,
    "UseWhereClausesInForLoops": false,
    "ValidateDocumentationComments": false
  },
  "spacesAroundRangeFormationOperators": false,
  "tabWidth": 8,
  "version": 1
}

ビルドと実行

ビルドと実行には Xcode 同様 destination が必要。ステータスバーにdestinationが表示されている。まだ指定していない場合はこのような表示がされているだろう。

設定せずこのままで実行してもよい。destinationを聞かれるので答える。設定済みなら聞かれない。

実行はコマンドパレットでSweetPad: Build & Run (Launch)から。

またはSweetPadのBuildビューでスキーマ名の右側にある▶️から実行する。

そうするとdestinationを聞かれる。

シミュレータと実機が並んでいる。実行させるデバイスを選ぼう。

事前にdestinationを指定しておきたい。その場合はSweetpadのDestinationsビューから右クリックで選択できる。

各種シミュレータ、各種デバイス(実機)がセクションに分かれて表示されており、選びやすい。

コマンドパレット SweetPad: Select destinationからも選択可能。

ビルドや実行、デバッグの出力を見やすくするため、xcbeautifyのインストール推奨。Toolsビューからインストールできる。

デバッグ

デバッグビューの Run and Debug ボタンを押してデバッグできる。

こんな感じでデバッグできる。

毎回聞かれるのが鬱陶しい場合は Run and Debug のすぐ下にある create a launch.json file を押して.vscode/launch.jsonを生成してもらおう。

.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "sweetpad-lldb",
      "request": "launch",
      "name": "Attach to running app (SweetPad)",
      "preLaunchTask": "sweetpad: launch",
    },
  ],
}

テスト

テストはここから実行する。

さいごに

SweetPad拡張機能の使いやすさがすごい。ちょっとしたことでつまずくことのないようにあれこれ用意してくれていることに感心した。おもてなしの心を感じる。しばらく使ってみようと思う。

Happy swifting!

Resources

SweetPad拡張機能

https://marketplace.visualstudio.com/items?itemName=SweetPad.sweetpad

SweetPad公式ホームページ

https://sweetpad.hyzyla.dev
チュートリアルがある。Get Started ボタンからどうぞ

xcode-build-serverリポジトリ

https://github.com/SolaWing/xcode-build-server
これがなくては始まらない。

Discussion