📍

Swift バックグラウンドで位置情報を処理する

2025/01/11に公開

概要

  • iOSアプリ上で、バックグラウンドで位置情報を処理する

Step1

  • これらの情報を埋めてください
  • Targets > Info
    • Privacy - Location Usage Description
    • Privacy - Location Always and When In Use Usage Description

Step2

  • Targets > Signing & CapabilitiesにBackground Modesを追加し、これらにチェックを入れましょう

Step3

  • LocationManagerWithBackgroundクラスを作り、コードを書きましょう
import Foundation
import SwiftData
import CoreLocation
import Combine

@MainActor
public class LocationManagerWithBackground: NSObject, ObservableObject {

    @MainActor public static let shared = LocationManagerWithBackground()

    @MainActor private let session = CLBackgroundActivitySession()
    @MainActor private let manager = CLLocationManager()
    @MainActor private var cancellables: Set<AnyCancellable> = .init()
    @MainActor private(set) public var currentLocation: CLLocation?
    
    @MainActor
    public override init() {
        super.init()
    }
    
    @MainActor
    public func requestAuthorization() {
        manager.requestWhenInUseAuthorization()
        manager.requestAlwaysAuthorization()
    }
    
    @MainActor
    public func start() {
        for cancellable in cancellables {
            cancellable.cancel()
        }
        let task = Task {
            var count = 0
            for try await update in CLLocationUpdate.liveUpdates(.fitness) {
                currentLocation = update.location
                try await processOnUpdate(location: update.location)
            }
        }
        self.cancellables.insert(.init { task.cancel() })
    }
    
    @MainActor
    public func processOnUpdate(location: CLLocation?) async throws {
        guard let coordinate = location?.coordinate else {
            return
        }
        // ここで何かする
    }
}

Step4

  • 位置情報取得開始のコードを書きましょう
import SwiftUI
import UIKit

class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions
        launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
    ) -> Bool {
        let locationsHandler = LocationManagerWithBackground.shared
        locationsHandler.requestAuthorization()
        locationsHandler.requestAuthorization() // 1回目のrequestAuthorizationでは"アプリの起動中は許可"しか選択できないため、2回必要です。
        locationsHandler.start()
        return true
    }
}

@main
struct IchijouhouApp: App {
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

注意点

  • アプリのタスクを切ると位置情報は更新されません
  • 位置情報更新時のプログラムでエラーが起きると、位置情報の更新がストップし、バックグラウンドから再起動することはできません (ユーザーがサイドアプリを開く必要がある)

Discussion