iOS 공부하는 감자

iOS) 위치 정보 사용하기 (CoreLocation) 본문

iOS

iOS) 위치 정보 사용하기 (CoreLocation)

DongTaTo 2022. 8. 15. 23:46
반응형

iOS에서 앱을 사용하다 보면 아래와 같은 권한 요청 메시지를 자주 보게 되는데, 

보안을 중요하게 생각하는 애플이 위치를 포함한 카메라, 연락처 등 민감한 개인정보에 대해 사용자에게 먼저 권한을 부여받아야 사용할 수 있도록 만들어두었기 때문이다.

 

WWDC의 "what's new in Privacy"를 통해 보안을 점점 강화하고 있고, 사용자 중심으로 개발하는 것을 요구하고 있다.

때문에, 권한 설정이 올바르지 않은 앱은 출시 과정에서 리젝 사유가 된다.

 

리젝 당하는 경우

  1. 권한에 대한 필요성 Description이 명확하지 않은 경우
  2. 앱에 꼭 필요하지 않은 권한을 등록한 경우

 


 

여러 권한 중 위치 정보를 받아오기 위해서는 다음과 같은 순서로 작업하면 된다.

  1. info.plist에 필요한 권한 추가 & Description 작성
  2. import CoreLocation & CLLocation Manager 인스턴스 생성
  3. 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 추가
  4. 앱에 대한 위치 권한이 부여된 상태인지 확인하는 메서드 추가
  5. Delegate설정 & Delegate Protocol 채택 

 

 

1. info.plist

먼저 info.plist파일에 필요한 권한에 대한 내용을 추가해야 한다.

Value에 입력하는 문자열은 사용자에게 권한 요청 얼럿을 보낼 때, Description으로 사용된다.

권한이 필요한 이유를 명확하게 작성하지 않으면 리젝 사유가 된다. (ex. 위치 권한 주세요, 카메라 좀 쓸게요)

 

 

 

2. import CoreLocation & CLLocation Manager 인스턴스 생성

위치 서비스를 사용하기 위해 CoreLocation 프레임워크를 import 해야한다.

CLLocationManager : 앱에서 위치 관련 이벤트 전달을 시작하고 중지할 때 사용하는 객체

import CoreLocation


class ViewController: UIViewController {

    let locationManager = CLLocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

 

 

 

 

3. 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 추가

3-1. 디바이스 자체에 위치 서비스가 활성화 상태인지 확인한다.

CLLocationManager의 타입 메서드인 locationServicesEnabled()를 통해 활성화 상태 여부를 Bool값으로 반환받을 수 있다.

만약 사용자 디바이스가 위치 서비스를 비활성화한 경우, 위치에 대한 권한 요청 자체가 불가하기 때문에 시스템 설정에 가서 사용자가 직접 설정값을 변경할 수 있도록 유도해야 한다. 

 

 

3-2. 사용자 디바이스의 위치 서비스가 활성화 상태라면,  앱에 대한 권한 상태를 확인해야 한다.

앱에 대한 권한 상태는 CLAuthorizationStatus라는 열거형 타입으로 표현되며, iOS 14를 기점으로 이를 확인할 수 있는 코드가 변경되었기 때문에 버전에 따른 분기 처리가 필요하다.

func checkUserDeviceLocationServiceAuthorization() {
        
    // 3.1
    guard CLLocationManager.locationServicesEnabled() else {
    	// 시스템 설정으로 유도하는 커스텀 얼럿
        showRequestLocationServiceAlert()
        return
    }
        
        
    // 3.2
    let authorizationStatus: CLAuthorizationStatus
        
    // 앱의 권한 상태 가져오는 코드 (iOS 버전에 따라 분기처리)
    if #available(iOS 14.0, *) {
        authorizationStatus = locationManager.authorizationStatus
    }else {
        authorizationStatus = CLLocationManager.authorizationStatus()
    }
        
    // 권한 상태값에 따라 분기처리를 수행하는 메서드 실행
    checkUserCurrentLocationAuthorization(authorizationStatus)
}

 

 

 

 

4. 앱에 대한 위치 권한이 부여된 상태인지 확인하는 메서드 추가

사용자가 앱에 설정한 권한 상태를 매개변수로 입력받고 분기 처리하는 메서드를 생성한다. (3번에서 작성한 메서드에서 호출)

 

CLAuthorizationStatus (권한 상태를 나타내는 열거형 타입)

  • .notDetermined : 사용자가 권한에 대한 설정을 선택하지 않은 상태
  • .restricted : 위치 서비스에 대한 권한이 없는 상태 / 자녀 보호 기능과 같은 상황으로 디바이스 자체에 활성이 제한된 상태
  • .denined
    • 사용자가 앱에 대한 권한을 거부한 상태
    • 권한을 승인했었더라도 추후에 시스템 설정에서 비활성화한 경우
    • 사용자가 디바이스 전체에 대한 위치 서비스를 비활성화 한 경우
    • 비행기 모드와 같은 상황으로, 위치 서비스를 이용할 수 없는 상황
  • .authorizedAlways : 앱이 백그라운드 상태에서도 위치 서비스를 이용할 수 있도록 승인된 상태
  • .authorizedWhenInUse : 앱이 포그라운드 상태에서만 위치 서비스를 이용할 수 있도록 승인된 상태 (사용자가 앱을 사용 중일 때만 위치 서비스를 사용 가능)

 

desiredAccuracy (위치 데이터의 정확도 설정)

  • KCLLocationAccuracyBest : 가능한 최고 수준의 정확도
  • KCLLocationAccuracyKilometer : 킬로미터 기준의 정확도
  • KCLLocationAccuracyThreeKilometers : 3킬로미터 기준의 정확도

이것 말고도 다양한 기준이 있는데, 주의할 점은 위치 데이터를 정확하게 가져올수록 배터리가 빨리 소모될 수 있다는 것.

func checkUserCurrentLocationAuthorization(_ status: CLAuthorizationStatus) {
    switch status {
    case .notDetermined:
        // 사용자가 권한에 대한 설정을 선택하지 않은 상태
        
        // 권한 요청을 보내기 전에 desiredAccuracy 설정 필요
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        
        // 권한 요청을 보낸다.
        locationManager.requestWhenInUseAuthorization()
            
    case .denied, .restricted:
    	// 사용자가 명시적으로 권한을 거부했거나, 위치 서비스 활성화가 제한된 상태
        // 시스템 설정에서 설정값을 변경하도록 유도한다.
        // 시스템 설정으로 유도하는 커스텀 얼럿
        showRequestLocationServiceAlert()
        
    case .authorizedWhenInUse:
    	// 앱을 사용중일 때, 위치 서비스를 이용할 수 있는 상태
        // manager 인스턴스를 사용하여 사용자의 위치를 가져온다.
        locationManager.startUpdatingLocation()
        
    default:
        print("Default")
    }
}

 

 

사용자의 현재 위치를 가져오는 2가지 방법

  1. requestLocation()                : 한 번만 위치를 요청 (가져옴)
  2. startUpdationgLocation()   : 현재 위치를 지속적으로 요청 (가져옴) → 위 코드에서 사용

차이점은 한 번만 요청하여 가져오는지, 지속적으로 요청하는지에 있다.

startUpdatingLocation()을 실행하면 stopUpdatingLocation() 메서드를 통해 update를 중지하기 전까지 반복적으로 사용자의 위치를 가져온다. (사용자 위치가 변경되었거나.. 시간이 좀 지나거나 ..)

 

startUpdatingLocation()을 사용한 후, 더 이상 사용자의 위치를 받아올 필요가 없다면 stopUpdatingLocation()을 통해 불필요한 위치 업데이트를 멈춰주는 게 좋다.

 

 

 

5. Delegate설정 & Delegate Protocol 채택 

5-1. 2단계에서 생성한 CLLocationManager의 인스턴스에 delegate를 설정한다.

class ViewController: UIViewController {

    let locationManager = CLLocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        locationManager.delegate = self
    }
}

 

 

5-2. CLLocationManagerDelegate 프로토콜을 채택하고, Delegate 메서드를 구현한다.

프로토콜의 채택, 요구사항 메서드 구현은 Extension에서 처리하는 게 좋다.

 

 

CLLocationManagerDelegate Methods

  • 사용자의 위치를 성공적으로 가져왔을 때 호출 : didUpdateLocations
  • 사용자의 위치를 가져오지 못한 경우 호출        : didFailWithError
  • 앱에 대한 권한 설정이 변경되면 호출               :
    • iOS 14 이상 ) locationManagerDidChangeAuthorization
    • iOS 14 미만 ) didChangeAuthorization
extension ViewController: CLLocationManagerDelegate {
    
    // 사용자의 위치를 성공적으로 가져왔을 때 호출
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        // 위치 정보를 배열로 입력받는데, 마지막 index값이 가장 정확하다고 한다.
        if let coordinate = locations.last?.coordinate {
            // ⭐️ 사용자 위치 정보 사용
        }
        
        // startUpdatingLocation()을 사용하여 사용자 위치를 가져왔다면
        // 불필요한 업데이트를 방지하기 위해 stopUpdatingLocation을 호출
        locationManager.stopUpdatingLocation()
    }
    
    
    
    
    // 사용자가 GPS 사용이 불가한 지역에 있는 등 위치 정보를 가져오지 못했을 때 호출
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print(#function)
    }
    
    
    
    
    // 앱에 대한 권한 설정이 변경되면 호출 (iOS 14 이상)
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        // 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 호출
        checkUserDeviceLocationServiceAuthorization()
    }
    
    // 앱에 대한 권한 설정이 변경되면 호출 (iOS 14 미만)
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        // 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 호출
        checkUserDeviceLocationServiceAuthorization()
    }
}

 

☝️ 권한 상태가 변경되었을 때 호출되는 메서드의 경우, CLLocationManager의 인스턴스가 생성되어, Delegate가 설정되는 시점에도 1회 호출됨

 

 

 

지금까지 작성한 코드들은 다음의 순서로 실행된다.

  1. 권한 상태가 변경되었을 때, 호출되는 메서드가 실행 (locationManagerDidChangeAuthorization)
  2. 사용자 디바이스의 위치서비스 활성화 여부 확인하는 메서드 실행 (checkUserDeviceLocationServiceAuthorization)
  3. 앱에 대한 권한 상태를 바탕으로 분기 처리하는 메서드 실행 (checkUserCurrentLocationAuthorization)
    1. 권한 상태가 notDetermined인 경우, 사용자에게 권한을 요청한 후 1번으로 되돌아감
    2. 사용자 위치를 받을 수 있는 경우, requestLocation 또는 startUpdating 메서드를 사용하여 위치 정보를 받음
  4. 위치 정보를 가져오면 호출되는 메서드를 통해 필요한 처리 (didUpdateLocations)

 

반응형

 


 

추가)

CLAccuracyAuthorization (iOS 14 ~)

사용자가 앱에 대해 설정한, 위치 정확도 수준을 알려준다. 
iOS 14 이후로 사용자는 위치 서비스에 권한을 승인할 때, "정확한 위치" 설정을 켜거나 끌 수 있는데, 사용자가 어떤 설정으로 위치 서비스를 승인했는지 확인할 수 있다.

 

CLAccuracyAuthorization은 열거형으로 구성되어 있다.

  • case fullAccuracy :
    • 사용자가 "정확한 위치" 설정을 켠 상태
    • 앱이 완전한 정확도로 위치 데이터에 접근할 수 있다.
  • case reducedAccuracy
    • 사용자가 "정확한 위치" 설정을 끈 상태
    • 앱이 낮은 정확도로 위치 데이터에 접근할 수 있다.
// CLLocationManager의 인스턴스를 통해 접근
locationManager.accuracyAuthorization

 

 

 

디바이스의 시스템 설정으로 유도하는 커스텀 얼럿

func showRequestLocationServiceAlert() {
    let requestLocationServiceAlert = UIAlertController(title: "위치 정보 이용", message: "위치 서비스를 사용할 수 없습니다.\n디바이스의 '설정 > 개인정보 보호'에서 위치 서비스를 켜주세요.", preferredStyle: .alert)
    let goSetting = UIAlertAction(title: "설정으로 이동", style: .destructive) { _ in
        if let appSetting = URL(string: UIApplication.openSettingsURLString) {
            UIApplication.shared.open(appSetting)
        }
    }
    let cancel = UIAlertAction(title: "취소", style: .default) { [weak self] _ in
        async { await self?.reloadData() }
    }
    requestLocationServiceAlert.addAction(cancel)
    requestLocationServiceAlert.addAction(goSetting)
    
    present(requestLocationServiceAlert, animated: true)
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

'iOS' 카테고리의 다른 글

iOS) Remote Notification (feat. Firebase)  (0) 2022.10.12
iOS) nib, awakeFromNib  (0) 2022.08.14
iOS) Run Loop  (0) 2022.08.08
iOS) ViewController에서 TableViewCell 내부의 Button Action 처리하기  (2) 2022.07.27
iOS) CollectionView - FlowLayout  (0) 2022.07.20