Hướng dẫn lấy user's location

Permissions

Apple cho phép developers có thể lấy được user’s location bằng cách sử dụng CoreLocation Framework. Tuy nhiên, app phải được user cho phép sử dụng location services bằng cách đồng ý request user’s permission khi mở lên. Có 2 loại authorization:

  • When In Use chỉ cho phép app lấy location khi đang mở lên
  • Always có thể lấy được location khi app đang ở backgroud Trong các versions trước, request permission để dùng location service không rõ ràng. Ta chỉ cần tạo 1 instance CLLocationManager và sử dụng đoạn code sau là có thể trigger hệ thống để nhắc user xác thực location service nếu user chưa approve hoặc deny trước đó.
import CoreLocation

let manager = CLLocationManager()
if CLLocationManager.locationServicesEnabled() {
    manager.startUpdatingLocation()
}

Hiện tại, request permission và bắt đầu dùng location service là hành động riêng biệt. Cụ thể, có 2 hàm khác nhau khi request permissions requestWhenInUseAuthorizationrequestAlwaysAuthorization.

if CLLocationManager.authorizationStatus() == .notDetermined {
    manager.requestAlwaysAuthorization()
}
if CLLocationManager.authorizationStatus() == .notDetermined {
    manager.requestWhenInUseAuthorization()
}

Nếu user đồng ý cho app sử dụng location service sẽ có 1 delegate xác định trạng thái authorization

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    if status == .authorizedAlways || status == .authorizedWhenInUse {
        manager.startUpdatingLocation()
        // ...
    }
}

Ngoài ra, ta cần đăng ký request location trong Info.plist. Có 2 key là NSLocationWhenInUseUsageDescriptionNSLocationAlwaysUsageDescription. Nếu ta gọi requestWhenInUseAuthorization hay requestAlwaysAuthorization mà không chỉ định trong plist sẽ không có thông báo show ra cho user. . Enable background mode cho location trong tab Capabilities.

Xử lý Location Service

Tiếp theo, ta khởi tạo CLLocationManager - 1 object chịu trách nhiệm về location data

    import CoreLocation

    fileprivate lazy var locationManager: CLLocationManager = {
        let manager = CLLocationManager()
        manager.desiredAccuracy = kCLLocationAccuracyBest
        
        manager.allowsBackgroundLocationUpdates = true
        manager.pausesLocationUpdatesAutomatically = false
        manager.delegate = self
        manager.requestAlwaysAuthorization()
        return manager
    }()

Có nhiều cách tùy chỉnh để lấy location và sẽ ảnh hưởng đến lượng pin tiêu thụ:

  • distanceFilter: lấy location theo khoảng cách là meter.
  • desiredAccuracy: lấy theo độ chính xác của location (kCLLocationAccuracyBestForNavigation, kCLLocationAccuracyBest, kCLLocationAccuracyNearestTenMeters, kCLLocationAccuracyHundredMeters, kCLLocationAccuracyKilometer, kCLLocationAccuracyThreeKilometers).
  • activityType: chỉ định type của user activity (other, automotiveNavigation, fitness, otherNavigation) Để lấy thông tin location ta cần implement delegate sau:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.max(by: { (location1, location2) -> Bool in
            return location1.timestamp.timeIntervalSince1970 < location2.timestamp.timeIntervalSince1970}) else { return }
}

Sau khi có location, ta thực hiện hiển thị lên bản đồ

    func didUpdateTrackingLocation(_ location: CLLocation) {
        
        locations.append(location)
        
        if let polyline = currentPolyline {
            mapView.remove(polyline)
        }

        currentPolyline = self.polyline(locations: locations, title: "location")
        mapView.add(currentPolyline!)
    }
    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer = MKPolylineRenderer(overlay: overlay)
        renderer.strokeColor = UIColor.blue.withAlphaComponent(0.8)
        renderer.lineWidth = 2.0
        
        return renderer
    }
}

Kết quả: