Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> iOS

วิธีใช้ Swift รันใน Background เพื่อดึงตำแหน่งที่ตั้งปัจจุบัน

รับ Location ใน Background ต้องตั้งค่า Capabilities, Info.plist, และ CLLocationManager ให้ถูกต้อง

1. Capabilities (Xcode Project Settings)

  • Signing & Capabilities → + Capability → Background Modes
  • ติ๊ก Location updates

2. Info.plist Keys


<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>แอปต้องการตำแหน่งที่ตั้งเพื่อฟีเจอร์ X แม้ขณะรันพื้นหลัง</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>แอปต้องการตำแหน่งที่ตั้งขณะใช้งานแอป</string>
<key>NSLocationAlwaysUsageDescription</key> <!-- iOS 10 และก่อน -->
<string>แอปต้องการตำแหน่งที่ตั้งตลอดเวลา</string>

3. โค้ด CLLocationManager


import CoreLocation

class LocationManager: NSObject, CLLocationManagerDelegate {
    static let shared = LocationManager()
    private let manager = CLLocationManager()
    private var continuation: AsyncStream.Continuation?
    
    private override init() {
        super.init()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.allowsBackgroundLocationUpdates = true // สำคัญ!
        manager.pausesLocationUpdatesAutomatically = false // ไม่หยุดอัตโนมัติ
        manager.showsBackgroundLocationIndicator = true // แสดง Indicator สีน้ำเงิน
    }
    
    func requestPermission() {
        manager.requestAlwaysAuthorization() // ขอ Always Permission
    }
    
    func startUpdating() {
        manager.startUpdatingLocation()
        manager.startMonitoringSignificantLocationChanges() // ประหยัดแบต
    }
    
    func stopUpdating() {
        manager.stopUpdatingLocation()
        manager.stopMonitoringSignificantLocationChanges()
    }
    
    // Delegate
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        continuation?.yield(location)
        // ส่งไป Server ที่นี่
        sendLocationToServer(location)
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Location error: \(error)")
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedAlways: print("Authorized Always")
        case .authorizedWhenInUse: print("Authorized When In Use")
        case .denied, .restricted: print("Denied")
        case .notDetermined: print("Not Determined")
        @unknown default: break
        }
    }
    
    // Async Stream สำหรับ Modern Swift
    var locationStream: AsyncStream {
        AsyncStream { continuation in
            self.continuation = continuation
        }
    }
    
    private func sendLocationToServer(_ location: CLLocation) {
        // Background Task สำหรับ Network
        var taskID: UIBackgroundTaskIdentifier = .invalid
        taskID = UIApplication.shared.beginBackgroundTask { 
            UIApplication.shared.endBackgroundTask(taskID)
        }
        
        // POST Location to API
        // ...
        
        UIApplication.shared.endBackgroundTask(taskID)
    }
}

การใช้งาน


// ใน AppDelegate หรือ SceneDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions...) {
    LocationManager.shared.requestPermission()
    LocationManager.shared.startUpdating()
}

// ใช้ Async Stream
Task {
    for await location in LocationManager.shared.locationStream {
        print("Location: \(location.coordinate)")
    }
}

Best Practices

  • Significant Location Changes - ประหยัดแบตมากกว่า (Update เมื่อเคลื่อนที่ ~500m+)
  • Background Task - ใช้ beginBackgroundTask สำหรับ Network Call
  • Battery - ตั้ง desiredAccuracy ให้เหมาะสม (ไม่ใช่ kCLLocationAccuracyBest ตลอดเวลา)
  • Privacy - แจ้งผู้ใช้ชัดเจน ว่าทำไมต้องการ Location Always