SSAC iOS 앱 개발자 데뷔과정 - 15

Sangwon Shin·2021년 10월 21일
0

SSAC

목록 보기
13/19

👮🏻‍♂️ Authorization & Privacy

iOS는 사용자에게 Data Privacy 에 대해 더 많은 통제권을 주는 형태로 변화하고 있습니다. 간단한게 사용자의 위치를 사용하는 앱을 살펴보겠습니다.

어떻게 위의 그림처럼 사용자의 위치에 접근할 수 있을까요?
이는 사용자의 휴대폰의 설정에서 위치 서비스가 활성화 되어 있고, 앱에서 사용자의 위치에 접근할 수 있는 권한을 얻었기 때문입니다.

만약 사용자의 휴대폰에 위치서비스가 비활성화 되어 있다면, 위의 그림처럼 앱에서 사용자의 위치에 접근할 수 없습니다.

그리고, 사용자의 휴대폰에 위치서비스가 활성화 되어 있더라도, 앱에서 위치에 접근할 수 있는 권한이 없다면 위의 그림처럼 사용자의 위치에 접근할 수 없습니다.
❗️ iOS 13 부터 한번만 허용, iOS 14 정확한 위치 설정이 추가 되었습니다.

전체적인 권한 설정을 그림으로 나타내면 아래와 같습니다. (강의자료 내용입니다.)


위치권한을 이용해서 사용자의 위치를 지도에 띄어주는 예제를 확인해보겠습니다.
❗️ 위치권한이 없다고 해서 지도를 사용하지 못하는 것은 아닙니다.

  • info.plist 설정

우선, 앱에서 사용자의 위치 정보에 대한 액세스 요청하는 이유를 사용자에게 알려주는 메시지를 info.plist 를 통해 작성해야 합니다.

우리는 사용자가 앱을 사용할 때, 사용자의 위치에 접근할 수 있어야 하기 때문에 Location when in Use Usage Description 에 사용자에게 위치 접근 허용 하도록 유도하는 메시지를 작성합니다.
❗️ 우리가 사용하는 권한에 대해서만 사용해야 합니다. (리젝 사유가 됩니다.)


사용자의 위치 정보를 지도에 표현하기 위해서 Mapkit 을 사용합니다.

  • import Mapkit -> 위치를 지도에 표현
  • import CoreLocation -> 위치정보를 관리
  • let locationManager = CLLocaationManager()
  • locationManager.delegate = self

설정을 마치고, extension 을 통해서 CLLocationManager 에서 프로토콜로 설정된 메서드들을 구현합니다.


모든 케이스에 대한 설정이 필요합니다.
먼저, 사용자의 휴대폰에 위치 설정이 켜저있는가?를 시작으로 분기점을 잡겠습니다.

// iOS 버전에 따른 분기 처리와 iOS 위치 서비스 여부 확인
    func checkUserLocationServicesAuthorization() {
        
        let authorizationStatus: CLAuthorizationStatus
        
        if #available(iOS 14.0, *) {
            authorizationStatus = locationManager.authorizationStatus //iOS 14 이상만 사용가능
        }
        else {
            authorizationStatus = CLLocationManager.authorizationStatus() // iOS 14 미만
        }
        
        
        //iOS 위치 서비스 확인
        if CLLocationManager.locationServicesEnabled() {
            //권한 상태 확인 및 권한 요청가능 -> 8번 메서드 실행
            checkCurrentLocationAuthorization(authorizationStatus)
        }
        //아예 시스템에서 위치 기능을 끈 경우
        else {
            print("iOS 위치 서비스를 켜주세요!")
        }
    }

우선, authorizationStatus 는 앱 내에서의 권한을 의미합니다.
iOS 14 이상에서 위치에 대한 권한은 정확도 설정이 추가되었기 때문에 추가적인 선택옵션이 존재하므로 따로 설정이 필요합니다. (열거형에 선택 조건이 추가된 형태?)

그리고 현재 사용자의 휴대폰에서 위치 설정이 켜져 있다면, 현재 앱 내에서 위치 권한을 확인하는 함수로 가게 됩니다.
해당 함수로 넘어가서 현재 권한 상태에 따라 분기점이 또 나뉘게 됩니다.

그리고 만약 사용자의 휴대폰에서 위치 설정이 꺼져 있다면, 앱 내에서 위치 권한을 요청할 수 없기 때문에 print 구문을 출력하고 종료됩니다.
Alert 을 띄워서 위치서비스를 활성화 해달라고 하는 첫번째 예제 사진 상황입니다.


그럼, 사용자의 위치 설정이 켜저있어서, 현재 앱 내에서의 위치 권한을 확인하는 함수로 넘어왔을 때를 살펴보겠습니다.

func checkCurrentLocationAuthorization(_ authorizationStatus: CLAuthorizationStatus) {
        
        switch authorizationStatus {
        case .notDetermined:
           locationManager.desiredAccuracy = kCLLocationAccuracyBest
           
// 앱을 사용하는 동안에 대한 위치 권환 요청
            locationManager.requestWhenInUseAuthorization() 
// 위치 접근 시작! (같이 나와야함)
            locationManager.startUpdatingLocation()
            
        case .restricted, .denied:
            print("Denied, 설정으로 유도")
            
        case .authorizedWhenInUse:
            locationManager.startUpdatingLocation()
            
        case .authorizedAlways:
            print("Always")
            
        @unknown default:
            print("Default")
        }
        
        if #available(iOS 14.0, *) {
            //정확토 체크: 정확도 감소가 되어 있을 경우, 1시간 4번으로 제한
            let accurancyState = locationManager.accuracyAuthorization
            switch accurancyState {
            case .fullAccuracy:
                print("Full")
            case .reducedAccuracy:
                print("Reduce")
            @unknown default:
                print("Default")
            }
        }
    }

앱 내에서 위치 정보 권한에 대한 분기점은 크게

  • 아직 설정하지 않은 경우
  • 거절된 경우
  • 허용된 경우 (다양한 case)

iOS 14 이상의 버전에서는 위치의 정확도에 대한 옵션도 있기 때문에 추가적인 설정을 하는 것을 확인할 수 있습니다.

만약 앱 내에서 위치 정보 권한이 거절되어 있는 경우에는 Alert 을 띄워서 사용자에게 허용을 할 수 있는 설정창으로 넘어가는 것이 일반적입니다

그리고, 사용자가 위치 정보를 허용한 경우에는 startUpdatingLocation 을 통해서 위치 접근을 시작

해당 부분 동작은 다시 한번 확인해보겠습니다.

// 4) 사용자가 앱 내에서 위치 허용을 한 경우,
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        print("감사합니다. 허용해 주셔서")
        
        // 사용자가 앱 내에서 위치 허용을 해줬으니까 현재 위치를 지도의 중심으로 설정한다.
        if let coordinate = locations.last?.coordinate {
            let annotation = MKPointAnnotation()
            annotation.title = "나 여기"
            annotation.coordinate = coordinate
            mapView.addAnnotation(annotation)
            
            let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            mapView.setRegion(region, animated: true)
            
            //10. 중요!!
            locationManager.stopUpdatingLocation()
            
        }
        
        //얘기치 못한 오류 방지 (비행기 모드로 변환되었을 경우) <-> 5번함수와의 기능
        else {
            print("Location Cannot Find")
        }
    }

사용자가 앱 내에서 위치권한을 허용한 경우, 위치를 받아서 지도상에 annotation 을 찍어 주도록 구현했습니다.

이때도, 위치권한은 허용되어 있지만 위치 정보를 가져오지 못하는 예외 경우에 대해서도 처리를 해주어야 합니다!

stopUpdatingLocation() 이 구현되어 있으면, 앱을 킬 때 위치가 계속해서 수정되지 않고 고정되어 있습니다. 
실제 기기에 앱을 넣고 테스트한 결과, 위치가 변경되지 않았습니다.
해당 메서드를 빼고 테스트 하고 다시 결과를 올리겠습니다.

// 5) 위치 접근에 실패한 경우(여기에 사용자가 앱내 위치 정보를 거부한것도 포함되는건가?)
// 위치접근을 허용했으나 위치 정보 조회에 실패한 경우 지도에서 어떤화면을 보여줄 것인가?
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        
        print("허용 안했어? 왜 오류가 나!")
        
        //위치 허용하지 않은 경우, 건국대 근처 스터디 카페
        let location = CLLocationCoordinate2D(latitude: 37.541366, longitude: 127.067187)
        let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
        let region = MKCoordinateRegion(center: location, span: span)
        mapView.setRegion(region, animated: true)
        
        let annotation = MKPointAnnotation()
        annotation.title = "눈물의 스터디 현장"
        annotation.coordinate = location
        mapView.addAnnotation(annotation)
    }

사용자의 위치 정보 접근에 실패한 경우, 위의 메서드가 호출됩니다.

허용했으나 오류가 나는 경우, 위치 허용을 하지 않은 경우 모두 해당 메서드가 호출되는건지 정확하게 모르겠습니다. (그럼 위에서 else 로 분기처리한것과 어떤차이가?..)

// 6) iOS 14 이상
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        print("승인상태 변경")
        checkUserLocationServicesAuthorization()
    }
    
    // 7) iOS 14 미만
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        print("승인상태 변경")
        checkUserLocationServicesAuthorization()
    }

권한 상태가 변경된 경우에는, 위의 메서드가 실행됩니다.
즉, 권한 상태가 변경되면 위치 권한을 다시 확인 하게 됩니다.

어떻게 변경되는것을 알 수 있을까요? 인터럽트 처럼 설정되어 있는걸까요?
추후에 정확한 동작 원리를 알게되면 정리하도록 하겠습니다.

🏷 P.S.

복잡합니다... 하지만 천천히 보면 이해할 수 있습니다.
아직까지는 아리송(?) 한 부분들이 있지만 다른 예제들을 다뤄 보면서 알게되면 다시 정리하는 시간을 가질 수 있도록 하겠습니다!

profile
개발자가 되고싶어요

0개의 댓글