본문 바로가기

ios

Location updates in Background Modes

위치정보를 Background Modes에서 가져오는 것을 구현해보겠습니다.

다음 그림과 같이 Capabilities에서 Location updates를 체크합니다.

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>NSLocationWhenInUseUsageDescription</key>
	<string>When In Use Usage</string>
	<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
	<string>Always and When In Use Usage</string>
</dict>
</plist>

2개다 정의해야 되면 하지 않으면 콘솔에 다음과 같은 오류 메시지가 나옵니다.

This app has attempted to access privacy-sensitive data without a usage description.
The app's Info.plist must contain both “NSLocationAlwaysAndWhenInUseUsageDescription” and “NSLocationWhenInUseUsageDescription” keys with string values explaining to the user how the app uses this data

필요한 Label, Map Kit View를 Storyboard에 넣고 다음과 같이 코딩하면 Background에서도 위치정보를 수집할 수 있습니다.

//
//  MapViewController.swift
//  Sample
//
//  Created by francis on 2020/04/13.
//  Copyright © 2020 Aircook. All rights reserved.
//

import UIKit
import MapKit
import RealmSwift

class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {

    @IBOutlet weak var mapView: MKMapView!
    
    @IBOutlet weak var infoLabel1: UILabel!
    @IBOutlet weak var infoLabel2: UILabel!
    
    @IBOutlet weak var statusLabel: UILabel!
    
    let locationManger = CLLocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        infoLabel1.text = ""
        infoLabel2.text = ""
        statusLabel.text = ""
        
        locationManger.delegate = self
        // 정확도를 최고로 설정
        locationManger.desiredAccuracy = kCLLocationAccuracyBest
        
        // 업데이트 이벤트가 발생하기전 장치가 수평으로 이동해야 하는 최소 거리(미터 단위)
        locationManger.distanceFilter = 100
        // 앱의 사용중과 관계없이 위치 서비스에 대한 사용자의 권한을 요청
        locationManger.requestAlwaysAuthorization()
        // 앱이 suspend 상태일때 위치정보를 수신받는지에 대한 결정
        locationManger.allowsBackgroundLocationUpdates = true
        // location manager가 위치정보를 수신을 일시 중지 할수 있는지에 대한 결정
        locationManger.pausesLocationUpdatesAutomatically = false
        
        //위치 업데이트 시작
        //locationManger.startUpdatingLocation()
        
        mapView.delegate = self
        mapView.showsUserLocation = true
        
    }

    func goLocation(latitude: CLLocationDegrees, longitude: CLLocationDegrees, delta span: Double) -> CLLocationCoordinate2D{
        let coordinate2d = CLLocationCoordinate2DMake(latitude, longitude)
        let coordinateSpan = MKCoordinateSpan(latitudeDelta: span, longitudeDelta: span)
        let coordinateRegion = MKCoordinateRegion(center: coordinate2d, span: coordinateSpan)
        mapView.setRegion(coordinateRegion, animated: true)
        return coordinate2d
    }
    
    func setAnnotation(latitude: CLLocationDegrees, longitude: CLLocationDegrees, delta span: Double, title strTitle: String, subtitle strSubTitle: String, pinColor color: UIColor) {
        let annotaion = ColorPointAnnotation()
        annotaion.coordinate = goLocation(latitude: latitude, longitude: longitude, delta: span)
        annotaion.title = strTitle
        annotaion.subtitle = strSubTitle
        annotaion.pinTintColor = color
        mapView.addAnnotation(annotaion)
    }
    
    // MARK: - CLLocationManagerDelegate
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        let lastLocation = locations.last
        let latitude = (lastLocation?.coordinate.latitude)!
        let longitude = (lastLocation?.coordinate.longitude)!
        
        Logger.debug("latitude is \(latitude), longitude is \(longitude)")
        
        //새로운 객체를 만들고, 자동증가값 PK를 셋팅한다.
        let locationInfo: LocationInfo = LocationInfo()
        locationInfo.id = locationInfo.autoIncrementKey()
        locationInfo.latitude = String(format: "%.7f", latitude)
        locationInfo.longitude = String(format: "%.7f", longitude)
        
        //Realm 저장
        let realm = try! Realm()
        
        try! realm.write {
            realm.add(locationInfo, update: .all)
        }

        //map에 annotation 추가
        setAnnotation(latitude: latitude, longitude: longitude, delta: 0.05, title: "위치감지", subtitle: "자동감지", pinColor: UIColor.orange)

        // delta값은 지도의 크기를 정하는데, 값이 작을수록 확대되는 효과가 있습니다. delta를 0.01로 하였으나 1의 값보다 지도를 100배로 확대해서 보여 줄 것입니다.
        _ = goLocation(latitude: latitude, longitude: longitude, delta: 0.01)
        
        CLGeocoder().reverseGeocodeLocation(lastLocation!, completionHandler: {
            (placemarks, error) -> Void in
            
            self.infoLabel1.text = "현재 위치"
            
            if let country = placemarks?.first?.country {
                var address = country
                if let locality = placemarks?.first?.locality {
                    address += " "
                    address += locality
                }
                if let thoroughfare = placemarks?.first?.thoroughfare {
                    address += " "
                    address += thoroughfare
                }
                
                self.infoLabel2.text = address
            }
            
        })
        
        //위치정보가 업데이트되는 것을 멈추게 합니다.
        //locationManger.stopUpdatingLocation()
    }
    
    // MARK: - MKMapViewDelegate
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        
        Logger.debug("mapView:viewFor:")
        
        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotation") as? MKPinAnnotationView

        if annotationView == nil {
            annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myAnnotation")
        } else {
            annotationView?.annotation = annotation
        }

        if let annotation = annotation as? ColorPointAnnotation {
            annotationView?.pinTintColor = annotation.pinTintColor
        }

        return annotationView
    }
 
    @IBAction func controlChanged(_ sender: UISegmentedControl) {
        if sender.selectedSegmentIndex == 0 {
            setAnnotation(latitude: 37.551325, longitude: 126.988199, delta: 0.05, title: "N서울타워", subtitle: "서울특별시 용산구 남산공원길", pinColor: UIColor.blue)
            self.infoLabel1.text = "보고 계신 위치"
            self.infoLabel2.text = "N서울타워"
        }
        else if sender.selectedSegmentIndex == 1 {
            setAnnotation(latitude: 37.482750, longitude: 127.000020, delta: 0.05, title: "효령대군릉", subtitle: "서울특별시 서초구 효령로", pinColor: UIColor.blue)
            self.infoLabel1.text = "보고 계신 위치"
            self.infoLabel2.text = "효령대군릉"
        }
        else if sender.selectedSegmentIndex == 2 {
            setAnnotation(latitude: 37.578052, longitude: 126.976898, delta: 0.05, title: "경복궁", subtitle: "서울특별시 종로구 세종로", pinColor: UIColor.blue)
            self.infoLabel1.text = "보고 계신 위치"
            self.infoLabel2.text = "경복궁"
        }
    }
    
    @IBAction func startButtonTouched(_ sender: Any) {
        Logger.debug("start button touched")
        locationManger.startUpdatingLocation()
        statusLabel.text = "기록중.."
    }
    
    @IBAction func stopButtonTouched(_ sender: Any) {
        Logger.debug("stop button touched")
        locationManger.stopUpdatingLocation()
        statusLabel.text = ""
    }
    

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */
}

class ColorPointAnnotation : MKPointAnnotation {
    var pinTintColor: UIColor?
}

실행화면은 다음과 같습니다.

 

다음 블로그글을 보면 위치정보에 대한 보다 많은 정보를 알 수 있다.
How to track user's location with high accuracy
https://medium.com/how-to-track-users-location-with-high-accuracy-ios

'ios' 카테고리의 다른 글

Xcode 에서 GitLab 연동하기  (0) 2020.04.22
위치서비스를 위한 권한 상태값 가져오기  (0) 2020.04.20
Migration in Realm  (0) 2020.04.18
Use Realm in Swift  (0) 2020.04.17
Tab Bar 사라지게 하는 방법  (0) 2020.04.03