【iOS】アプリに Widget を追加するまでに引っかかった点まとめ

作成中のアプリに Widget を追加するにあたり、ありとあらゆるつまづきポイントに引っかかったためメモ

## 環境
- Xcode Version 12.3 (12C33)
- iOS 13 以上

## 行いたいこと
- 出勤・退勤を打刻するアプリの作成
- ウィジェットから出勤・退勤打刻を行えるようにしたい
    - その前段階としてまずウィジェットを表示するところから

## 引っかかった点
1. BundleId の設定
2. クラス名
3. ウィジェットの追加ができない
4. Podfile の設定
5. iOS 13 でもビルドが通るようにしたい
6. 実機でビルドできない


## 1. BundleId の設定
- 元のアプリの bundle Id は「com.test-inc.MyApp.develop」および「com.test-inc.MyApp」とする
- 「com.test-inc.MyApp.Widget.develop」として生成した

### 問題点1(Bundle Id が不正)
以下のようなエラーが発生

```
error: Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.
```

### 解決法1
bundle Id を `com.test-inc.MyApp.develop.Widget` とした

### 問題点2（Scheme ごとの Bundle Id 設定忘れ）
develop 環境ではビルドが通るが product 環境ではビルドが通らない

### 解決法2
`WidgetExtension` ターゲット > `Build Settings` > `Packaging > Product Bundle Identifier` から Scheme ごとの Bundle Id を設定

## 2.クラス名

### 問題点
ウィジェット名を「Widget」として作成したところ、自動生成された以下のコード部分でエラー

```swift:Widget.swift
@main
struct Widget: Widget {
    let kind: String = "Widget"

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
            WidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}
```

```
'Widget' is annotated with @main and must provide a main static function of type () -> Void or () throws -> Void.
```

### 解決法
`Widget` は既に使われているため、構造体の名前を変更

```swift:Widget.swift
@main
struct TimeCardWidget: Widget {
    let kind: String = "Widget"

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
            WidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}
```

## 3. Podfile の設定
### 問題点
- シミュレータでビルド後ホーム画面に遷移しウィジェットの追加を試みても対象アプリのウィジェットが表示されない

### 解決法
- Podfile に以下を追加する

```Podfile:Podfile
target 'WidgetExtension' do
end
```

## 4. ウィジェットが Swift UI のUI確認画面に表示されない

### 問題点1. Scheme の選択
- Scheme として `WidgetExtension` を選択しているとウィジェットのUIのプレビューを表示しようとするとエラーが表示される

### 解決法1
- Scheme として `MyAppDevelop` を選択

### 問題点2. `Architectures` の設定
ビルド時に以下のようなエラーが発生

```
(省略) building for iOS Simulator, but linking in object file (省略)
```

### 解決法2

- TARGETS から SampleApp と WidgetExtension の Build Settings を変更
    - `Architectures > Build Active Architecture Only > Develop` を `Yes`
    - `Excluded Architectures > Develop` に `Any iOS Simulator SDK` を追加し `arm64` に変更
- WidgetExtension のみ変更してもビルドエラーが消えなかった

## 5. iOS 13 でもビルドが通るようにしたい

### 問題点
Widget 機能自体は iOS 14 以降のものだがアプリの対象は iOS 13以上

### 解決法
- MyApp の `Development Info` から 対象を iOS 13.0 以上に設定
- WidgetExtension の `Development Info` から対象を iOS 14.0 以上に設定

## 6. 実機でビルドできない
### 問題点
- 実機をつなげてビルドしようとすると以下のエラー

```
"WidgetExtension" requires a provisioning profile. Select a provisioning profile in the Signing & Capabilities editor.
```

### 解決法
- WidgetExtension 用に Provisioning Profile を発行する必要あり
    - 今回は開発環境と本番環境で２つ必要
    - [Widget に利用する Provisioning Profile は App Developer サイトで手動で発行する必要があるらしい](https://stackoverflow.com/questions/65403747/ios-widget-provision-profile-issue-how-to-correct-distribute-with-ios-widgetkit)

## 参考
- [iOS 14 アプリケーションのWidgetの追加（通常のWidget、Intentsを使用した構成可能なWidget)](https://qiita.com/MaShunzhe/items/d29d58f2204f082d8109)
- [アルを iOS 14 のウィジェットに対応した際いろいろハマったので共有します](https://note.com/koogawa/n/n29ee3fcebfe0)
- [[iOS] Xcode12.0で「building for iOS Simulator, but linking in object file ... for architecture arm64」エラーの対処法](http://blog.be-style.jpn.com/article/187942746.html)
- [iOS開発で環境ごとにアイコンやアプリ名、コード等を切り分けるオレオレプラクティス](https://qiita.com/KazaKago/items/2835d76ced43f913c31d)
- [iOS Widget provision profile issue: how to correct distribute with iOS WidgetKit
](https://stackoverflow.com/questions/65403747/ios-widget-provision-profile-issue-how-to-correct-distribute-with-ios-widgetkit)