ラベル Xcode の投稿を表示しています。 すべての投稿を表示
ラベル Xcode の投稿を表示しています。 すべての投稿を表示

2026年9月3日木曜日

Xcode26 で ipa ファイルを作成する方法

Xcode26 で ipa ファイルを作成する方法

概要

なぜか xcode からだとエンロール関係でエラーになるので CLI を使いましょう

環境

  • macOS 26.6.2
  • Xcode 26.6 (17F113)
  • iPhone 15

事前準備

  • Xcode に無料の Developer Account でログイン
  • Xcode で開発用の証明書作成
  • iPhone 実機の接続

ビルド: app フォルダの作成

xcodebuild -workspace ios-moributa.xcworkspace \
           -scheme ios-moributa \
           -sdk iphoneos \
           -configuration Release \
           -archivePath ./build/ios-moributa.xcarchive \
           archive

アーカイブ: ipa ファイルの作成

xcodebuild -workspace ios-moributa.xcworkspace \
           -scheme ios-moributa \
           -sdk iphoneos \
           -configuration Release \
           -archivePath ./build/ios-moributa.xcarchive \
           archive

配布設定ファイルの作成

  • vim ExportOptions.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://apple.com">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>development</string> <!-- 開発用証明書(iOS Development)で書き出す -->
    <key>signingStyle</key>
    <string>automatic</string>
</dict>
</plist>

最後に

ワークスペース名やスキーマ名は適宜変更してください

2025年6月3日火曜日

Github Actions で xcodebuild する方法

Github Actions で xcodebuild する方法

概要

Apple Sillicon 向けのビルドをする方法を紹介します

環境

  • macOS 15.5 (M1 Mac book air)
  • Xcode 16.2
  • xcodebuild 16.3

GithubActions 上では以下を使います
詳細は参考サイトに記載の情報を参照してください

  • macos-latest (macOS 15.4.1)
  • Xcode 16.2
  • xcodebuild 16.3.0.0.1.1742442376

build.yml

name: Build an app file

on:
  push:
    branches:
      - "master"

jobs:
  build:
    runs-on: macos-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true
      - name: Set up Xcode version
        run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
      - name: Install CocoaPods
        run: |
          gem install cocoapods
          pod repo update
          pod install
          xcodebuild -version
      - name: Build project
        run: |
          xcodebuild -workspace ios-fndb.xcworkspace \
                     -scheme ios-fndb \
                     -sdk iphonesimulator \
                     -destination 'platform=iOS Simulator,name=iPhone 16' \
                     clean build 

ポイント

xcode-select は必ず実行しましょう
できればローカルのマシンでビルドした Xcode と同じバージョンを選択してください
選択する macos-xxx によって使用できる Xcode のバージョンなどに差があるので注意してください

-sdk iphonesimulator がもしかしたら不要かもですが一応指定しています

あとはローカルで一度同じ xcodebuild コマンドを実行して成功することを確認しておきましょう

最後に

xcodebuild 用の Action もあるようなのですがそもそもベースのイメージを提供してくれているのでそれを使ってコマンドを実行するほうがいろいろなところに手が届くので良いかなと思います

参考サイト

2025年3月14日金曜日

iOS アプリを xcodebuild CLI コマンドでビルドする方法

iOS アプリを xcodebuild CLI コマンドでビルドする方法

概要

Xcode を起動しないでとりあえずビルドできるかチェックしたい場合に便利です
あとは CI などに使えます

環境

  • macOS 15.3.1
  • xcodebuild 16.2 (16C5032a)

コマンド

  • cd /path/to/project
  • xcodebuild -workspace project_name.xcworkspace -scheme project_name -destination 'platform=iOS Simulator,name=iPhone 16' clean build

成果物

以下のパスに app ファイルが作成されます

~/Library/Developer/Xcode/DerivedData/project_name-bpovlgogrctpyxcxsxxxxxxxcxon/Build/Products/Debug-iphonesimulator/project_name.app

Any iOS Device でビルドする方法

destination オプションで generic/platform=iOS だけを指定します

  • xcodebuild -workspace project_name.xcworkspace -scheme project_name -destination 'generic/platform=iOS' clean build

成果物のパスは以下に変わります

~/Library/Developer/Xcode/DerivedData/project_name-bpovlgogrctpyxcxsxxxxxxxcxon/Build/Products/Debug-iphoneos/project_name.app

トラブルシュート

Cocoapods を使ってサードパーティのライブラリを管理している場合はこちらの方法でサードパーティ側のバージョンも固定しなければいけない場合があります
固定する設定がなく Pods 側のビルドバージョンの変更を毎回 Xcode でやっていた場合には固定化する設定が必要になります

最後に

わざわざ Xcode を開いてビルドする必要がなくなるので Pods をアップデートして問題なくビルドできるかなどの確認作業を CLI のみで行うことができるようになります

参考サイト

2025年2月20日木曜日

Pod でインストールしたライブラリ側の IPHONEOS_DEPLOYMENT_TARGET を変更する方法

Pod でインストールしたライブラリ側の IPHONEOS_DEPLOYMENT_TARGET を変更する方法

概要

The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 12.0 to 18.2.99. (in target 'YouTubePlayer' from project 'Pods')

への対応です

環境

  • macOS 15.3.1
  • xcode 16.2

Podfile 編集

一番最後に追加すれば OK です

  • vim Podfile
post_install do | installer |
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
    end
  end
end

適用

  • pod update

確認

  • grep -ir 'IPHONEOS_DEPLOYMENT_TARGET'

最後に

pod install したときに上記の指定がないとライブラリ側でサポートしているバージョンが適用されます
プロジェクト側で指定しているバージョンよりも大きければ問題ないですが小さい場合には Pod 側のバージョンを上げる必要があるので注意しましょう

2024年10月24日木曜日

(サンプルコード) SwiftUI で RSS フィードリーダ

(サンプルコード) SwiftUI で RSS フィードリーダ

概要

ポイントは

  • Swift 純正の XMLParser と XMLParserDelegate を使い RSS 情報をパースする
  • XMLParser で CDATA があるときは専用のメソッドを使う
  • CDATA 内にある html をパースして更に必要な情報が取得できる
  • XML の必要な情報はモデルにして SwiftUI 上でバインドする

環境

  • macOS 15.0.1
  • Xcode 16.0

RSSFeedParser.swift

RSS フィードをパースする処理を管理するクラスです

import Foundation
import Combine

class RSSFeedParser: NSObject, ObservableObject, XMLParserDelegate {
    @Published var items: [RSSItem] = []
    
    // 各種要素を管理する変数
    private var currentElement = ""
    private var currentTitle = ""
    private var currentLink = ""
    private var currentDescription = ""
    private var currentContent = ""
    private var parsingItem = false
    
    // RSS フィールドをロードしパースを開始する
    func loadRSSFeed(url: URL) {
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else { return }
            
            let parser = XMLParser(data: data)
            parser.delegate = self
            parser.parse()
        }
        task.resume()
    }
    
    // XMLParser の Delegate メソッド、要素が見つかったら最初にコールされる
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        currentElement = elementName
        if elementName == "item" {
            currentTitle = ""
            currentLink = ""
            currentDescription = ""
            currentContent = ""
            parsingItem = true
        }
    }
    
    // XMLParser の Delegate メソッド、要素内の一般タグでも見つかったらコールされる
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        if parsingItem {
            if currentElement == "title" {
                currentTitle += string
            } else if currentElement == "link" {
                currentLink += string
            } else if currentElement == "description" {
                currentDescription += string
            }
        }
    }
    
    // XMLParser の Delegate メソッド、要素内の CDATA タグでも見つかったらコールされる
    func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {
        if parsingItem && currentElement == "content:encoded" {
            if let cdataString = String(data: CDATABlock, encoding: .utf8) {
                currentContent += cdataString
            }
        }
    }
    
    // XMLParser の Delegate メソッド、要素が終了したらコールされる
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "item" {
            let imageSrc = extractImageSrc(from: currentContent)
            let item = RSSItem(title: currentTitle, link: currentLink, description: currentDescription, content: currentContent, imageSrc: imageSrc)
            DispatchQueue.main.async {
                self.items.append(item)
            }
            parsingItem = false
        }
    }
  
    // CDATA から特定の html 属性情報を抽出するヘルパーメソッド
    func extractImageSrc(from htmlString: String) -> String? {
        let pattern = "<img[^>]+src=\"([^\"]+)\""
        
        if let regex = try? NSRegularExpression(pattern: pattern, options: []) {
            let range = NSRange(htmlString.startIndex..., in: htmlString)
            if let match = regex.firstMatch(in: htmlString, options: [], range: range) {
                if let srcRange = Range(match.range(at: 1), in: htmlString) {
                    return String(htmlString[srcRange])
                }
            }
        }
        return nil
    }
}

RSSItem.swift

RSS フィードをパースした結果のデータを管理するモデルクラスです

import Foundation
import SwiftUI

struct RSSItem: Identifiable {
    let id = UUID()
    let title: String
    let link: String
    let description: String
    let content: String  // html 付きの description
    let imageSrc: String?
}

ContentView.swift

SwiftUI で RSS フィードのモデル情報を表示します

import SwiftUI

struct ContentView: View {
    @StateObject private var rssFeedParser = RSSFeedParser()
    
    var body: some View {
        NavigationView {
            List(rssFeedParser.items) { item in
                VStack(alignment: .leading) {
                    Text(item.title)
                        .font(.headline)
                    if let imageSrc = item.imageSrc {
                        AsyncImage(url: URL(string: imageSrc)) { image in
                            image
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(height: 200)
                        } placeholder: {
                            ProgressView()
                        }
                    }
                    Text(item.description)
                        .font(.body)
                    Text(item.link)
                        .font(.subheadline)
                        .foregroundColor(.blue)
                }
            }
            .navigationTitle("RSS Feed")
            .onAppear {
                if let url = URL(string: "https://figmanendoroiddb.blog.fc2.com/?xml") { // RSS URLをここに設定
                    rssFeedParser.loadRSSFeed(url: url)
                }
            }
        }
    }
}

#Preview {
    ContentView()
}

testApp.swift

実行メインクラス

import SwiftUI

@main
struct testApp: App {  
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

最後に

SwiftUI を使った場合 UI とモデルのデータバインドがほぼ自動でやってくれるので楽です

表示するコンテンツの情報も SwiftUI 側で調整するだけで RSS フィードを取得する側のロジックでは全く気にする必要がないです

2024年10月23日水曜日

SwiftUI + BackgroundTasks で定期的にローカルプッシュを送信する方法

SwiftUI + BackgroundTasks で定期的にローカルプッシュを送信する方法

概要

アプリがバックグランドに移行した際にバックグランドタスクを使って定期的にローカルプッシュを送信してみます

環境

  • macOS 15.0.1
  • Xcode 16.0

Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>BGTaskSchedulerPermittedIdentifiers</key>
        <array>
                <string>com.example.app.refresh</string>
        </array>
        <key>UIBackgroundModes</key>
        <array>
                <string>fetch</string>
        </array>
</dict>
</plist>

サンプルコード

ポイントはタスクのイベントハンドラで (handleAppRefresh) で再キューイングしている部分とタスクが実行された際にローカルプッシュを送信している部分です

import SwiftUI
import BackgroundTasks

@main
struct testApp: App {
    // SwiftUI における AppDelegateの設定
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @Environment(\.scenePhase) private var scenePhase
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) {
            if scenePhase == .background {
                // バックグラウンドに入る際にタスクをスケジュール
                appDelegate.scheduleAppRefresh()
            }
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 通知の許可
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
            if granted {
                print("Notification permission granted")
            } else if let error = error {
                print("Notification permission error: \(error.localizedDescription)")
            }
        }
        // バックグラウンドタスクの登録、ここのコールバックメソッドは実際にタスクが実行される際に呼ばれます
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.app.refresh", using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
        return true
    }
    
    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
        // 10秒後に実行するようにしているが実際に実行されるのはアプリがバックグランドに移動してからタスクが実行可能になった10秒後に実行される
        // バックグランドに移動してから10秒後ではないので注意
        request.earliestBeginDate = Date(timeIntervalSinceNow: 10)
        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Unable to schedule app refresh: \(error)")
        }
    }
    
    // 実際にタスクが実行された際にタスクの実行結果のハンドリングを行うメソッド
    func handleAppRefresh(task: BGAppRefreshTask) {
        task.expirationHandler = {
            print("Task expired")
        }
        let operation = BlockOperation {
            // ローカルプッシュを送信
            self.sendLocalNotification()
            print("Background task is running")
        }
        operation.completionBlock = {
            task.setTaskCompleted(success: !operation.isCancelled)
            print("Task completed: \(operation.isCancelled ? "Cancelled" : "Success")")
            // タスク完了後、次のタスクをスケジュール
            self.scheduleAppRefresh()
        }
        OperationQueue().addOperation(operation)
    }
    
    // ローカルプッシュ行うメソッド
    func sendLocalNotification() {
        let content = UNMutableNotificationContent()
        content.title = "Background Task"
        content.body = "This is a local notification from the background task."
        content.sound = .default
        
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
        
        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("Error in scheduling local notification: \(error.localizedDescription)")
            }
        }
    }
}

最後に

iPhone 側のリソース状況にもよりますがだいたい 10分から15分に1回ローカルプッシュが来るようになります
あくまでもアプリがバックグランドにいる場合なのでキルされた場合などは来ないのでご注意ください

2024年10月21日月曜日

SwiftUI + BackgroundTasks を試してみた

SwiftUI + BackgroundTasks を試してみた

概要

SwiftUI で Delegate を定義してその中でバックグランド処理を定義します
アプリがバックグランドに移動するとタスクがキューイングされ実行されます

Swift の BackgroundTasks の最大のポイントは自分で実行する時間を制御できない点です
iOS 側のリソース状況によって実行時間がだいぶ変わるのですぐには実行されません

環境

  • macOS 15.0.1
  • Xcode 16.0

Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>BGTaskSchedulerPermittedIdentifiers</key>
        <array>
                <string>com.example.app.refresh</string>
        </array>
        <key>UIBackgroundModes</key>
        <array>
                <string>fetch</string>
        </array>
</dict>
</plist>

サンプルコード

実際にタスクが実行されるのは iOS 側の制御になるので指定の時間や指定の間隔で必ず実行されないことに注意です

import SwiftUI
import BackgroundTasks

@main
struct testApp: App {
    // SwiftUI における AppDelegateの設定
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @Environment(\.scenePhase) private var scenePhase
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) {
            if scenePhase == .background {
                // バックグラウンドに入る際にタスクをスケジュール
                appDelegate.scheduleAppRefresh()
            }
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // バックグラウンドタスクの登録、ここのコールバックメソッドは実際にタスクが実行される際に呼ばれます
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.app.refresh", using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
        return true
    }
    
    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
        // 10秒後に実行するようにしているが実際に実行されるのはアプリがバックグランドに移動してからタスクが実行可能になった10秒後に実行される
        // バックグランドに移動してから10秒後ではないので注意
        request.earliestBeginDate = Date(timeIntervalSinceNow: 10)
        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Unable to schedule app refresh: \(error)")
        }
    }
    
    // 実際にタスクが実行された際にタスクの実行結果のハンドリングを行うメソッド
    func handleAppRefresh(task: BGAppRefreshTask) {
        task.expirationHandler = {
            print("Task expired")
        }
        let operation = BlockOperation {
            print("Background task is running")
        }
        operation.completionBlock = {
            task.setTaskCompleted(success: !operation.isCancelled)
            print("Task completed: \(operation.isCancelled ? "Cancelled" : "Success")")
        }
        OperationQueue().addOperation(operation)
    }
}

最後に

今回のサンプルコードではアプリがバックグランドに移動するたびにタスクがキューイングされるので定期的に実行されるようなタスクではありません

そもそも iOS ではバックグランド処理はリソースを占有する処理になるため悪とされているので可能な限り使わないほうがいいのかなと思います

Debug -> Simulate Background fetch で実行して 15 分後くらいにデバッグログが表示されました

2024年10月16日水曜日

iOS アプリの entitlements を確認する方法

iOS アプリの entitlements を確認する方法

概要

コマンドで取得する方法を紹介します
ipa ファイルではなく app ファイルに対して行うのがポイントです

環境

  • macOS 15.0.1
  • Xcode 16.0

コマンド

  • codesign -d --entitlements :- app_name.app | xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>application-identifier</key>
    <string>your.app.identifier</string>
    <key>com.apple.developer.team-identifier</key>
    <string>xxxxxx</string>
    <key>get-task-allow</key>
    <true/>
  </dict>
</plist>

Xcode であれば

~/Library/Developer/Xcode/DerivedData/app_name-cuzoqdebxqlaksegeoxfmmsaetqz/Build/Products/Debug-iphoneos/app_name.app

に archive ビルドした app ファイルがあります

2024年10月13日日曜日

iOS アプリが必要としているパーミッションの一覧を取得する方法

iOS アプリが必要としているパーミッションの一覧を取得する方法

概要

アプリがどんなパーミッションを必要としているか確認するときに使えます
おまけでバージョン各種バージョン情報を取得するコマンドも掲載します

環境

  • macOS 15.0.1
  • Xcode 16.0

コマンド

for i in `find . -name "Info.plist" | grep -v 'Pods/' | grep -v 'Tests'`; do echo ${i}; plutil -p ${i} | grep 'UsageDescription'; done

CFBundleVersion を確認するコマンド

for i in `find . -name "Info.plist" | grep -v 'Pods/' | grep -v 'Tests'`; do echo ${i}; plutil -p ${i} | grep 'CFBundleVersion'; done

or

for i in `find . -name "*.pbxproj" | grep -v 'Pods/' | grep -v 'Tests'`; do echo ${i}; plutil -p ${i} | grep 'CURRENT_PROJECT_VERSION'; done

CFBundleShortVersionString を確認するコマンド

for i in `find . -name "Info.plist" | grep -v 'Pods/' | grep -v 'Tests'`; do echo ${i}; plutil -p ${i} | grep 'CFBundleShortVersionString'; done

or

for i in `find . -name "*.pbxproj" | grep -v 'Pods/' | grep -v 'Tests'`; do echo ${i}; plutil -p ${i} | grep 'MARKETING_VERSION'; done

デプロイターゲットバージョンを確認するコマンド

for i in `find . -name "*.pbxproj" | grep -v 'Pods/' | grep -v 'Tests'`; do echo ${i}; plutil -p ${i} | grep 'IPHONEOS_DEPLOYMENT_TARGET'; done

参考サイト

2024年10月9日水曜日

Xcode で double-quoted include エラー

Xcode で double-quoted include エラー

概要

対応方法を紹介します

環境

  • macOS 15.0.1
  • Xcode 16.0

Quoted include in Framework Header を No にする

プロジェクト本体と Pods の Build Settings の Quoted include in Framework Header を設定します

プロジェクト本体は No にし Pods 側を Yes にします

pod update

  • pod install
  • pod update

を実施します

Xcode 再起動

再起動します

動作確認

これでエラーが解消されるはずです

最後に

なぞのエラーはまだ出るようです

参考サイト

2024年10月7日月曜日

AltStore で公開するアプリを作成する方法

AltStore で公開するアプリを作成する方法

概要

ipa ファイルを作成する方法を紹介します
ポイントは既存の Apple Developer Program に登録していたメンバーシップ期限切れのアカウントを使わないことです

環境

  • macOS 15.0.1
  • AltServer 1.7.2
  • Xcode 16.0

AltServer および AltStore にログインした Apple ID で Xcode にログイン

すでに Apple Developer Program のメンバーシップが切れているユーザでログインしても ipa が作成できないので AltStore 用に新規で作成した Apple ID を使ってログインします

Settings -> Accounts

でログインしましょう
また開発用の証明書がない場合は

Settings -> Accounts -> Manage Certificates -> プラスボタン

で新規に証明書を作成しましょう

Bundle Identifier の確認

すでに公開していたアプリを AltStore 用にビルドする際に Bundle Identifier を変更する必要があります

アプリのビルド時に Team および Bundle Identitifier を調整しましょう

Apple Developer Program へのログイン

https://developer.apple.com/ へログインだけしましょう
Developer Program への登録は不要です

Any iOS Device にしてビルド

Product -> Archive -> Distribute App -> Custom -> Release Testing

.app フォルダをアーカイブして .ipa にする

  • mkdir ~/Downloads/Payload
  • cp -ipr ~/Library/Developer/Xcode/DerivedData/app_name-cuzoqdebxqlaksegeoxfmmsaetqz/Build/Products/Debug-iphoneos/app_name.app ~/Downloads/Payload

あとは Payload を圧縮して .zip -> .ipa に拡張子を変更するだけです

おまけ: .ipa を iPhone にダウンロードして AltStore に追加する

nginx を使います

  • brew install nginx
  • cp ~/Downloads/Payload.ipa /opt/homebrew/var/www/

iPhone 側で 192.168.1.100:8080/Payload.ipa (IP の部分は適宜変更してください) にアクセスし Chrome にダウンロードします

あとは AltStore を開いて MyApps -> プラスボタン -> Chrome 内の Payload.ipa を参照すれば AltStore 内にインストールできます

インストールしたアプリは普通に iPhone のホーム画面上にいます
またアプリを開く際に「信頼できないなんちゃら」という警告が出る場合は

設定 -> 一般 -> VPNとデバイス管理 -> デベロッパアプリ -> AltStore インストール時のアドレスを選択して信頼

をしてください

トラブルシューティング

個人の開発アカウントでは1週間に10個までしかアプリを追加できない

Your maximum App ID limit has been reached. You may create up to 10 App IDs every 7 days.

基本 AltStore にインストールするためには2つIDを使うことになります
また AltStore ですでに2つ使うので実質一週間で8IDしか使えないので一週間の間で ipa を作成できるのは4アプリになります

最後に

.ipa ファイルはただのアーカイブファイルだということに注意が必要です

.ipa ファイルが追加できる条件として AltServer と通信できている状態でないとインストールできないので注意してください

また AltStore に追加できるアプリは3つ以上は追加できない(AltStoreがあるので実質2つ)ので注意してください

参考サイト

2024年3月31日日曜日

iOS アプリを久しぶりにビルドしたときのメモ

iOS アプリを久しぶりにビルドしたときのメモ

概要

最終ビルドは 2021/7 頃のアプリになります
もともと Swift5 化はしていたプロジェクトになります

環境

  • Xcode 15.3
  • macOS 14.4.1

libarclite 対応

エラー

SDK does not contain 'libarclite' at the path '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a'; try increasing the minimum deployment target

対処方法

cocoapods などでインストールしたライブラリに対して行います
iOS Deployment Target を 12 以上にします
あまりメンテナンスされていないライブラリや古いライブラリの場合に発生します

  • Pods -> Project-> Build Settings -> iOS Deployment Target -> 12.0

ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES を inherited にする

エラー

cocoapods で表示される警告になります

[!] The `projectUITests [Debug]` target overrides the `ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES` build setting defined in `Pods/Target Support Files/Pods-project-projectUITests/Pods-project-projectUITests.debug.xcconfig'. This can lead to problems with the CocoaPods installation

対処方法

ビルド設定で ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES を $(inherited) を設定します

  • Target -> projectTests -> Build Settings -> Always Embed Swift Starndard Libraries -> $(inherited)

Sandbox オプションの無効化

エラー

データ保存系のライブラリで発生している気がします

Sandbox: rsync.samba(19313) deny(1) file-read-data /Users/user/Library/Developer/Xcode/DerivedData/project-bpovlgogrctpyxcxspcdgxydcxon/Build/Products/Debug-iphonesimulator/XCFrameworkIntermediates/Realm/Headers

対処方法

  • Project-> Build Settings -> User Script Sandboxing -> No

各種非推奨のコードなどの変更

Xcode が指摘してくれるおすすめ設定やコードリファクタを行いましょう

古いライブラリや存在していないライブラリの削除

例えば Firebase/AdMob はすでに存在していません
存在していないのですがなぜか使えて使っている場合は古い Firebase/Core が使われてしまうので危険です
ちゃんと代替の Google-Mobile-Ads-SDK を使う必要があります

+  pod 'Google-Mobile-Ads-SDK'
-  pod 'Firebase/AdMob'

最後に

定期的にビルドするのが大事です

2022年3月25日金曜日

SwiftUI を試してみたのでメモ

SwiftUI を試してみたのでメモ

概要

SwiftUI のチュートリアルの1章を試してみたの成果物と感じたことをメモしておきます

環境

  • macOS 11.6.5
  • Xcode 13.1

感想

  • 開発は Vue や React のように宣言的に行える (状態が変わると自動で描画し直すなど)
  • コンポーネントごとに SwiftUI ファイルを作成できる
  • Stack を使いこなせるといい UI ができそう
  • 既存の UIKit から SwiftUI へのコンバートは頑張ればできるみたい
  • ライブプレビューは便利だがある程度スペックのあるマシンでないと重くなる

以下成果物です

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            MapView().ignoresSafeArea(edges: .top).frame(height: 300)
            CircleImage().offset(y: -130).padding(.bottom, -130)
            VStack(alignment: .leading) {
                Text("現在地").font(.title)
                HStack {
                    Text("詳細1")
                    Spacer()
                    Text("詳細2")
                }.font(.subheadline).foregroundColor(.secondary)
                Divider()
                Text("現在地について").font(.title2)
                Text("説明")
            }.padding()
            Spacer()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

CircleImage.swift

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("test").resizable().frame(width: 150, height: 150).clipShape(Circle()).overlay(Circle().stroke(Color.white, lineWidth: 4)).shadow(radius: 7)
    }
}

struct CircleImage_Previews: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}

MapView.swift

import SwiftUI
import MapKit

struct MapView: View {
    @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 35.6834875, longitude: 139.7608643), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
    
    var body: some View {
        Map(coordinateRegion: $region)
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView()
    }
}

2022年3月22日火曜日

This operation can fail if the version of the OS on the device is incompatible with the installed version of Xcode.

This operation can fail if the version of the OS on the device is incompatible with the installed version of Xcode.

概要

実機デバック時に発生したので対応しました

環境

  • Xcode 13.2.1
  • iPhone 15.4

原因

Xcode 13.2.1 が iPhone15.4 でのビルドに対応していないのが原因です

DeviceSupport をインストールしてあげます

方法

  • wget https://github.com/filsv/iPhoneOSDeviceSupport/raw/master/15.4.zip
  • unzip 15.4.zip
  • sudo mv 15.4 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/

これで Xcode を再起動すれば OK です

参考サイト

2022年2月23日水曜日

Unable to process request - PLA Update available

Unable to process request - PLA Update available

概要

Xcode で実機ビルドしようとしたらタイトルのエラーが出ました
対処方法を紹介します

環境

  • macOS 11.6.2
  • Xcode 13.2.1

PLA に同意する

https://developer.apple.com/account/#!/overview/ から同意すれば OK

repair する

Xcode の「Signing & Capabilities」から repair すれば OK です

2022年2月8日火曜日

Xcode13.2.1 dyld: Symbol not found: ___chkstk_darwin 対応

Xcode13.2.1 dyld: Symbol not found: ___chkstk_darwin 対応

概要

Xcode13.2.1 でビルドしたアプリをiOS12 の実機で動かそうとすると dyld: Symbol not found: ___chkstk_darwin というエラーが発生しアプリがクラッシュします
どうやら Xcode のバグで Xcode13.3 で直っているそうなので試してみました

https://forums.swift.org/t/swift-concurrency-back-deploy-issue/53917/27

環境

  • macOS 11.6.3 -> 12.0.0
  • Xcode 13.2.1

Beta版のダウンロード

https://developer.apple.com/download/ からダウンロードします
DeveloperProgram に入っていないとダウンロードできません

Xcode_13_3_beta.xip というファイルがダウンロードできました
ファイルサイズが 10GB あるので注意してください

Beta版のインストール

ダウンロードした xip ファイルをダブルクリックしましょう
「デジタル署名書を検証中」が長いですがしばらく待ちましょう
そのあとで展開中になりプログレスバーが追われてば完了です

Xcode-beta.app が作成されるので Applications 配下に移動します

ビルドして動作確認

あとはビルドして動作確認すれば OK です

ビルドオプションを付与すれば大丈夫?

公式にもあるようなのでビルドオプションでも回避できるかもしれません

https://developer.apple.com/documentation/xcode-release-notes/xcode-13_2-release-notes https://learn.apptentive.com/knowledge-base/xcode-project-setup-guide/

最後に

Xcode13.3 は macOS12 (Monterey) が必要になります
もし Monterey がない場合は Xcode12 をダウンロードして同じようにインストールしてビルドしましょう

参考サイト