iOS APPの作り方。（With App Store ＆ AdMob & SwiftUI)

✳︎自分用メモです。随時更新
admobはバナータイプです。
SwiftUIです。
シミュレーターはadmobがテストモードになります。
App Connectの審査の時はテストモードのスクショは却下になります。
App Connect登録時にワーニング２つ出るのは、無視。
App ConnectのマーケティングURLは登録の必要あり。（admobの審査で必要：このページのトップのapp-ads.txt ファイルをクローリングする。）
Privacy-policyの作成にはここを使いました。
https://app-privacy-policy-generator.nisrulz.com

# 手順

## とりあえず名前をキープするため

1.Developerサイトで登録

https://developer.apple.com/account/resources/identifiers/bundleId/add/bundle

2.App　Connectでアプリの登録
とりあえずしないと名前が決定しない。名前は登録順らしい。名前が取れなきゃアイコンとかが決まらないので。

https://appstoreconnect.apple.com/apps

## コード作成上必要なIDを取得するため
3.admobでアプリ登録（追加）

https://admob.google.com/v2/home

広告ユニット（形式）が決まっていればそれも
この記事はバナー

## 作成開始(X Code)
4.XCodeでアプリを新規作成

5.ProjectのSigning ＆ Capabilities があっているか確認

6.言語設定をする。英語もあった方が良さそうなので残し、Japaneseを追加して、それをデフォルトにセットする。
 ![スクリーンショット 2025-06-22 18.17.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/14457e9e-f478-4cf0-b61b-4766458f9fd5.png)

## AdMob周りをしていきます。

https://developers.google.com/admob/ios/quick-start?hl=ja#import_the_mobile_ads_sdk

7.Google Mobile Ads の Swift パッケージをインストール
File->Add Package Dependencities... で下記URL
https://github.com/googleads/swift-package-manager-google-mobile-ads.git

8.Info.plistの設定(admobのAPPID）
最初はファイルがないので、Infoの画面で登録　登録するとファイルができる。
適当なキーのマウスでロールオーバーすると右に＋が表示されるのでクリック。
キーに　GADApplicationIdentifier　Valueにadmobのapp idを入力
Infoファイルができるので"Open as">"Source code"で開く
６行目（APP ID の書いてあるStringタグ）以降に下記の部分を追加。Googleの説明とファイル形式が違う？（Googleのファイルは一部省略してある？）。

```
<key>SKAdNetworkItems</key>
<array>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>cstr6suwn9.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>4fzdc2evr5.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>2fnua5tdw4.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>ydx93a7ass.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>p78axxw29g.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>v72qych5uu.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>ludvb6z3bs.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>cp8zw746q7.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>3sh42y64q3.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>c6k4g5qg8m.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>s39g8k73mm.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>3qy4746246.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>f38h382jlk.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>hs6bdukanm.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>mlmmfzh3r3.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>v4nxqhlyqp.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>wzmmz9fp6w.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>su67r6k2v3.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>yclnxrl5pm.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>t38b2kh725.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>7ug5zh24hu.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>gta9lk7p23.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>vutu7akeur.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>y5ghdn5j9k.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>v9wttpbfk9.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>n38lu8286q.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>47vhws6wlr.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>kbd757ywx3.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>9t245vhmpl.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>a2p9lx4jpn.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>22mmun2rn5.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>44jx6755aq.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>k674qkevps.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>4468km3ulz.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>2u9pt9hc89.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>8s468mfl3y.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>klf5c3l5u5.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>ppxm28t8ap.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>kbmxgpxpgc.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>uw77j35x4d.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>578prtvx9j.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>4dzt52r2t5.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>tl55sbb4fm.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>c3frkrj4fj.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>e5fvkxwrpn.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>8c4e2ghe7u.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>3rd42ekr43.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>97r2b46745.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>3qcr597p9d.skadnetwork</string>
  </dict>
</array>
```
## 多言語化の設定
9.New File From TemplateからResourceグループのString　Catalogを選択（ファイル名はそのまま）
![スクリーンショット 2025-06-22 17.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/4e9a77da-ae88-46a0-96d3-e08116ec1b30.png)

10.New File From TemplateからResourceグループのString　Catalogを選択ファイル名を"InfoPlist"で作成(大文字小文字を間違えないよう）
![スクリーンショット 2025-06-22 17.45.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/a2961ff2-e2b9-464e-9bde-f72aa457bacb.png)

## Admobのための追加コード
11.GoogleMobileAdsConsentManager.swiftファイルの作成
https://developers.google.com/admob/ios/quick-start?hl=ja#import_the_mobile_ads_sdk
のGoogleMobileAdsConsentManager.swiftファイルをプロジェクトに取り込み
（テンプレートで新規Swiftファイルを作って、ペースト）ホームページでは一部しか表示されていないので注意

12.BannerContentView.swift　ファイルの作成
https://developers.google.com/admob/ios/banner?hl=ja
のBannerContentView.swift　を取り込む　（テンプレートで新規Swiftファイルを作って、ペースト）ホームページでは一部しか表示されていないので注意
３９行目のIDを変更。（シミュレータはデバッグしか表示されない）
２８行目の Privateを削除
```
import GoogleMobileAds
import SwiftUI

struct BannerContentView: View {
  let navigationTitle: String

  // [START add_banner_to_view]
  var body: some View {
    Spacer()
    // Request an anchored adaptive banner with a width of 375.
    let adSize = currentOrientationAnchoredAdaptiveBanner(width: 375)
    BannerViewContainer(adSize)
      .frame(width: adSize.size.width, height: adSize.size.height)
      // [START_EXCLUDE silent]
      .navigationTitle(navigationTitle)
    // [END_EXCLUDE]
  }
  // [END add_banner_to_view]
}

struct BannerContentView_Previews: PreviewProvider {
  static var previews: some View {
    BannerContentView(navigationTitle: "Banner")
  }
}

// [START create_banner_view]
struct BannerViewContainer: UIViewRepresentable {
  typealias UIViewType = BannerView
  let adSize: AdSize

  init(_ adSize: AdSize) {
    self.adSize = adSize
  }

  func makeUIView(context: Context) -> BannerView {
    let banner = BannerView(adSize: adSize)
    // [START load_ad]
    banner.adUnitID = "ca-app-pub-8132641067593791/2804805858"
    banner.load(Request())
    // [END load_ad]
    // [START set_delegate]
    banner.delegate = context.coordinator
    // [END set_delegate]
    return banner
  }

  func updateUIView(_ uiView: BannerView, context: Context) {}

  func makeCoordinator() -> BannerCoordinator {
    return BannerCoordinator(self)
  }
  // [END create_banner_view]

  class BannerCoordinator: NSObject, BannerViewDelegate {

    let parent: BannerViewContainer

    init(_ parent: BannerViewContainer) {
      self.parent = parent
    }

    // MARK: - GADBannerViewDelegate methods

    func bannerViewDidReceiveAd(_ bannerView: BannerView) {
      print("DID RECEIVE AD.")
    }

    func bannerView(_ bannerView: BannerView, didFailToReceiveAdWithError error: Error) {
      print("FAILED TO RECEIVE AD: \(error.localizedDescription)")
    }
  }
}
```

13.IDFAの設定
https://developers.google.com/admob/ios/privacy/idfa?hl=ja

Infoでキー　”NSUserTrackingUsageDescription”　Valueは日本がデフォルトなら”「許可」するとお客様に最適化された広告が表示されます。＂を登録する。（NSUserTrackingUsageDescription　は　”Privacy -Tracking Usage Description”になります。嫌な場合は右クリックでRaw key and Valueにすれば良い）
![スクリーンショット 2025-06-22 18.14.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/fe4d265b-78c3-48c6-8228-9c792306a85d.png)

14.一旦ビルドする。
ローカリゼーションファイルに反映するため。反映されていればビルド不要

15.InfoPlistのローカリゼーションファイルを開く
IDFA　Messageを英語は”This identifier will be used to deliver personalized ads to you.”日本は”「許可」するとお客様に最適化された広告が表示されます。”にします。Defaultの言語は設定できないかも。。。。（できるプロジェクトとできないプロジェクトがある？？？ので不明）
![スクリーンショット 2025-06-22 18.19.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/da199d6e-70b1-405c-93a0-f6c0f04800b3.png)

16.Admobのホームページの左のプライバシーとメッセージのIDFAメッセージを登録

17.メインとなるコードに追加
```
import SwiftUI
import SwiftData
//以下の５件のインポート追加
import UserMessagingPlatform
import AppTrackingTransparency
import AdSupport
import GoogleMobileAds
import Foundation


//以下のクラスを追加
class AppDelegate: UIResponder, UIApplicationDelegate {
    let shared = GoogleMobileAdsConsentManager()
    
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        debugPrint("lunch option \(launchOptions)")
        shared.gatherConsent { status in
            debugPrint("status \(status)")
        }
        return true
    }
}

@main
struct EasyCSVApp: App {
//以下の１行を追加
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate  
    var body: some Scene {
        
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: CSVData.self)
    }
    init(){
        debugPrint("Program Started")
    }
}

```
この時点で実行するとIDFAの画面が表示されると思います。
シミュレータや実機で一旦動かしてAppがある場合は一旦削除しないと出ない時もあります。

18.あとはコードを書きます。
バナーを表示したいところで
```import GoogleMobileAds``` 行と
```
  let adSize = currentOrientationAnchoredAdaptiveBanner(width: 375)
            BannerViewContainer(adSize)
                .frame(width: adSize.size.width, height: adSize.size.height)
```

App Storeの登録は色々記事に書いてあるので省略プライバシーポリシーに注意

# 参考
## 多言語化したい文字列は
String(localized: "文字列") と書けばLocalizableファイルに自動で上がる。（Textの文字列とかは何もしなくても上がります）
Localizableファイルに反映するのに１回ビルドする必要がある時もあります。

## AdMobの審査
設定をきちんとして、チェックボタンを押しても無駄です。１日待ちましょう（クローリングが終わるまではチェックできません。チェックボタン（確認ボタン）を押さなくても正常なら、問題なく登録されてメールが届きます。