Danny의 iOS 컨닝페이퍼
article thumbnail

[TIL #22] 2023 / 05 / 11

LocationManager에 대한 사용법을 간단히 정리하려고 합니다.

 

 

info.plist

기본적으로 사용자 위치 정보를 받기 위해서는 먼저 plist를 설정해줘야 합니다.

간단히 어떤 것이 있나 알아봅시다.

 

아래 설정들은 모두 기본적으로

 "앱이 사용자의 위치 정보에 대한 액세스를 요청하는 이유를 사용자에게 알리는 메시지입니다."

 

[Privacy - Location Always and When In Use Usage Description]

Background에서 실행되는 동안에만 적용

공식문서

 

[Privacy - Location Always Usage Description]

Background 실행되는 동안에만 적용

iOS 11+ 이상부터는 대신, Location Always and When In Use Usage Description를 사용해줘야 합니다.

공식문서

 

[Privacy - Location When In Use Usage Description]

Foreground에서 실행되는 동안에만 적용

공식문서

 

[Privacy - Location Usage Description]

macOS에서 사용됩니다.

공식문서

 

 

참고로 requestAlwaysAuthorization 메서드를 사용하기 위해서는

Location Always and When In Use Usage Description, Location When In Use Usage Description

위의 두 설정을 같이 사용해줘야 합니다.

 

아무튼 상황에 맞게 사용하면 될 것 같습니다.

 

 

사용방법

기본적으로 위치 관련 작업을 하기 위해선, Core Location 프레임워크를 사용해줘야 합니다.

그 후, 사용자의 위치값은 CLLocationManager를 사용해 받아주면 됩니다.

 

일단, CLLocationManager을 상속받아, 사용자 위치를 받아올 수 있는 Custom class를 만들어 봤습니다.

설명은 주석을 참고해 주세요.

final class LocationManager: CLLocationManager, CLLocationManagerDelegate {
        
    typealias FetchLocationCompletion = (CLLocationCoordinate2D?, Error?) -> Void

    // 동작을 담아주기 위해 클로저를 만들어 줌
    private var fetchLocationCompletion: FetchLocationCompletion?
    
    override init() {
        super.init()
        
        self.delegate = self
        // 위치관련 정보를 받기위해선 무조건 호출되어야 합니다. 
        // 설정한 plist에 따라서 requestAlwaysAuthorization로도 사용이 가능합니다.
        // 이 메서드만 따로 빼서, 실행해줘도 되요!
        self.requestWhenInUseAuthorization()
        // 위치 정확도 (여기선 배터리 상황에 따른 최적의 정확도로 설정)
        self.desiredAccuracy = kCLLocationAccuracyBest
    }
    
    /// 현재 사용자 위치 업데이트
    ///
    /// 이 메서드를 여러 번 호출해도 새 이벤트가 생성되지 않으므로, 새로운 이벤트를 받으러면, 꼭 stopUpdatingLocation을 사용 후 사용해야 합니다.
    override func startUpdatingLocation() {
        super.startUpdatingLocation()
    }
    
    /// 위치 업데이트 생성 중지
    override func stopUpdatingLocation() {
        super.stopUpdatingLocation()
    }

    /// 현재 위치를 딱 한번만 전달합니다. (그런데 위치를 받는게 뭔가 느리다)
    override func requestLocation() {
        super.requestLocation()
    }
    
    /// 현재 위치를 받고 컴플리션을 통해 동작을 실행하는 메서드
    func fetchLocation(completion: @escaping FetchLocationCompletion) {
        self.requestLocation()
        // completion 동작을 didFetchLocation 동작에 담는다.
        self.fetchLocationCompletion = completion
    }
}

extension LocationManager {
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // 사용자의 최신 위치 정보를 가져옵니다.
        guard let location = locations.first else { return }
        let coordinate = location.coordinate
        
        // coordinate 값을 갖고 저장 한, 동작을 실행
        self.fetchLocationCompletion?(coordinate, nil)
        // 위의 실행 후 클로저 초기화
        self.fetchLocationCompletion = nil
    }
    
    // 잠재적인 오류에 응답하기 위해서 생성
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Unable to Fetch Location (\(error))")
        
        // 에러발생 시 저장된 값을 갖고 동작을 실행
        self.fetchLocationCompletion?(nil, error)
        // 위의 실행 후 클로저 초기화
        self.fetchLocationCompletion = nil
    }
    
    // 현재 인증 상태 확인
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedAlways , .authorizedWhenInUse:
            print("Location Auth: Allow")
            // 인증 메세지 늦게 클릭하면 처음 업데이트 되는 데이터를 못받게 됨.
            // (그래서 권한을 확인으로 설정 시, 위치를 한 번 받아 줬다)
            self.startUpdatingLocation()
        case .notDetermined , .denied , .restricted:
            print("Location Auth: denied")
            self.stopUpdatingLocation()
        default: break
        }
    }
}

 

사용

class ViewController: UIViewController {
        
    var locationManager = LocationManager()
    var location: CLLocationCoordinate2D?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        setupLocation()
    }
    
    // 현재 위치 정보 저장
    func setupLocation() {
        locationManager.fetchLocation { [weak self] (location, error) in
            self?.location = location
        }
    }
    
    @objc func buttonHandler(_ sender: UIButton) {
        print(location)
    }
}

 

 

마무리

좋은 코드인지는 모르겠지만, 일단은 정상적으로 사용자 위치 값을 갖고 올 수 있습니다. ㅎ

아 그리고 전체 코드는 GitHub 를 참고해 주세요. 

 

필요한 때 복사해서 사용하면 편리할 것 같습니다.

 

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!