EEYatHo 앱 깎는 이야기

WWDC19 ) CoreLocation 새로운 소식 (iOS13) 본문

iOS, Swift/WWDC, Session, Docu

WWDC19 ) CoreLocation 새로운 소식 (iOS13)

EEYatHo 2024. 11. 14. 15:52
반응형

영상 링크

예제 링크

 

개요

  • 위치 권한 요청 팝업을 하나로 간소화함 (개발자 입장에선 복잡해짐..)
  • Before

  • After

  • 항상 허용 권한이 지연
  • “사용하는 중 허용”의 권한이 향상
  • ”임시 권한”들이 새로나옴
  • 새로운 Becon Raging을 구체적인 예시, 방법까지 들면서 설명할 것임

 

새로워진 권한 요청

  • 권한 팝업에서 유저가 허용 안함을 선택하면, 앱에서는 위치에 액세스할 수 없으므로, 위치 사용 의향이 가장 강할 때 표시하세요
  • 새로운 팝업에는 가장 중요한 “항상 허용”이 없습니다.
  • 아래 코드를 실행하면, 권한 팝업이 뜨고, 유저의 선택에 따라 “사용하는 동안 허용” 권한을 받습니다.
  • 하지만, requestAlwaysAuthorization로 요청했다면, “임시 항상 허용” 권한도 함께 받습니다.
var locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization() // 임시 항상 권한도 받음
locationManager.requestWhenInUseAuthorization() // 임시 항상 권한 안줌

 

“임시 항상 허용” 권한

  • 파란색은 유저에게 표시되는 것이고, 초록색은 앱에 표시되는 내용입니다.
    • 임시 항상 허용 권한에 따라, 유저와 앱의 표시가 달라집니다.
    • 유저는 “사용 하는 중 허용” 으로 보이지만, 앱에서는 delegate로 “항상 허용”을 받습니다.
      (다시 볼 때 헷갈릴 수 있어서 첨언. 유저가 실제로 항상허용까지 해줘야 이벤트가 수신됨.)

  •  
    • 나중에 더 자세히 설명하겠지만, 지오펜스를 설정하여 사용자의 위치에 따라 백그라운드 동작을 수행할 수 있습니다.
      • 이후 지오펜스 영역 입출입에 따라 이벤트가 발생하면, CoreLocation은 바로 대리자에게 전달하지 않고, 임시로 보관
      • 사용자에게 “항상 허용” 으로 업그레이드할지 묻기에 적절한 시간을 기다림
      • 적절한 타이밍에 업그레이드 팝업이 노출
      • “항상 허용” 하거나, “사용하는 중 허용” 둘 중에 하나를 고르면 어느쪽이든 임시 권한은 끝남
      • “항상 허용”을 받으면 이벤트를 수신하며, “사용하는 중 허용”이라면 받지 못함

 

 

  • 세부사항을 이야기해 봅시다
    • 먼저 앱이 임시 항상 허용 권한이 있는 동안, 이벤트를 모니터링하고 생성하지만, 실제로 권한을 받지 않는 이상 앱에 전달되지 않음을 명심
    • 사용자가 앱에 항상 허용 권한을 부여할 수 있는 팝업은 나중에 발생합니다
    • 이 프로세스는 한번만 할 수 있습니다.
  • 앱에 이벤트가 전달되는 시점과 삭제되는 시점을 이야기해보겠습니다.
    • 궁극적으로, 앱이 항상 권한을 받으면 전달되고, 사용중 권한만 받으면 전달되지 않습니다.
    • 아직 선택하지 않은 경우에도, 전달되지 않습니다.
    • CoreLocation은 사용자가 바쁘지 않다고 생각되는 시간을 기다려서, 유저의 이해력을 극대화하고 방해받는 다는 느낌을 최소화합니다.
      • 이 기간동안 더 많은 이벤트가 발생할 경우, 해당 이벤트가 이전 이벤트를 대체합니다
      • CoreLocation은 이 이벤트가 너무 오래되어도 삭제합니다
        • 그 이유는, 더이상 사용자의 관점이나 심리상, 이해할 수 없는 위치 데이터이기 때문입니다.
      • 이 때문에 많은 사용 사례에서, 매우 중요하다고 생각하는 첫 이벤트가 삭제될 것이라는 것을 알고 있지만, 항상 허용 권한은 그만큼 큰 신뢰가 필요하다고 생각하였습니다.

 

다른 플랫폼들

  • 항상 권한 부여의 가용성과 처리가 플랫폼 마다 다르다는 점
  • tvOS는 사용중 허용만 지원
  • watchOS는 사용중 허용만이 필요
    • watchOS는 백그라운드 런타임에 대한 액세스가 매우 제한적이며, 대부분의 API가 시작 동작을 제공하지 않기 때문
  • macOS는 마찬가지로 항상 권한을 지원하지 않지만, 프롬프트가 자동으로 표시되므로 권한을 요청할 필요가 없음
    • 즉, Mac에서 iPad 앱을 사용할 경우, 사용중 권한과 항상 권한이 실질적으로 동일하다는 것

 

“사용 중 허용” 에 대하여

  • iOS 12
    • 사용중 권한이 있는 앱은, 위치 업데이트를 수신하고, 비콘을 ranging하고, 파란 화살표를 표시하며 포그라운드에서 실행한 뒤 백그라운드에서 위치 업데이트를 수신할 수 있었습니다.
    • 하지만, 직접 백그라운드 위치 업데이트를 시작할 수 없으며, 백그라운드에서 모니터링하는 API들을 사용할 수 없습니다.
    • 그렇다면 기능들을 어떻게 좀더 일관되게 분류할 수 있을까요?
    • 위 3개 서비스는 앱이 사용중일때만 작동하며, 아래 4개 서비스는 앱을 사용하지 않을때도 위치 서비스를 제공할 수 있다는 겁니다.

  • iOS 13
    • iOS 13은 사용중인지 아닌지에만 집중하였습니다.
    • 어떤 기능이든, “사용 중 허용”이면 앱 “사용 중”에만 제공, ”항상 허용”이면 앱 “사용 중”이 아닐때도 제공.

 

언제 “사용 중” 인가

  • 그렇다면 언제 앱이 “사용중” 일까요?
    • 기본적으로, 앱이 포그라운드로 전환되고, 이후 백그라운드로 전환될 떄 까지 사용중입니다.
      (다시볼 떄 햇갈릴 수 있어서 첨언. 설정에 따라 달라짐.)
    • 백그라운드에서 몇초정도 더 사용 중으로 간주되지만, 이는 사용자가 앱을 떠나기 직전에 필요한 이벤트를 다루는 유예 기간이며 매우 짧으므로 의존 x
    • 이후 포그라운드로 전환될 때 까지 사용중이 아니며, 이 프로세스가 반복됩니다.

  • Xcode에서 앱이 지원하는 백그라운드 모드 목록에 location update를 추가하고, 아래의 추가 설정을 진행하면 “사용 중” 범위가 넓어짐
    • 앱이 포그라운드에서 위치 업데이트를 시작하고, locationManager의 allowsBackgroundLocationUpdates를 true로 설정하면,
    • 앱이 백그라운드에 있을 때에 파란 표시가 나타나며 “사용중”으로 판단 (그림에서 넓어진 진한 초록색)
    • 이후 포그라운드에서 allowsBackgroundLocationUpdates를 false로 한다면, 백그라운드는 사용중이 아니게 됩니다.

 

“항상 허용” 권한이 필요한 경우

  • iOS 13에서 모든 CoreLocation API는 “사용 중 허용”으로 백그라운드까지 사용할 수 있습니다.
    • 그리고 로컬 알림이나, 파란 표시로 적절하게 상황을 유지할 수 있습니다.
    • 즉 이제 유저가 앱을 시작하지 않고도 자동화 해야할 경우에만 “항상” 권한이 필요합니다.

 

“한번 허용” 권한 ( == ”임시 사용 중 허용” 권한)

  • 임시 권한을 얻는 또다른 방법
  • 팝업 가운데 있는 “한번 허용”
  • 앱의 권한 부여 상태와, VR(?) 팝업으로 인한 일부 전환을 살펴보겠습니다. (VR이 무슨 약자?)
  • 이 임시 권한이 어디에 해당하는지 그림에서 확인할 수 있습니다.

iOS 12부터 시작하겠습니다.

  • 앱이 처음 포그라운드가 되면 상태는 .notDetermined 입니다.
  • 유저의 팝업 선택에 따라, 거부되거나, “사용 중 허용”으로 갔다가 “항상 허용”으로 가거나, 바로 “항상 허용”으로 갈 수도 있습니다.

이제 iOS 13입니다

  • denied는 제외하고 보겠습니다.
  • “항상 허용”으로 가기 위해서는 반드시 “임시 항상 허용” 권한을 거치게됩니다.
    • “임시 항상 허용” 권한에서는 “사용 중 허용” 권한으로 다운그레이드 될 수 있습니다.
    • 이후 다시는 “항상 허용”으로의 권한 업그레이드 팝업을 띄울 수 없습니다.
  • 위에서 말한 “한번 허용”을 선택시, “임시 사용 중 허용” 권한을 얻게됩니다.
    • “사용 중 허용” 권한과 마찬가지로, LocationManager는 대리자 콜백을 받게 됩니다.
    • 이후 앱이 “사용 중”이지 않은 상태로 돌아가면, 권한은 .notDetermined로 돌아갑니다.
    • 이 때는, 업그레이드 팝업과 다르게, 다시 권한 획득 팝업을 띄울 수 있습니다.

 

한번 허용을 가지고 녹색 차트 사례를 다시 살펴보겠습니다.

  • 유저가 포그라운드 였다가, “아주잠깐” 백그라운드로 가고 다시 포그라운드로 돌아와 앱을 사용한다면, 유저는 쭈욱 “임시 사용 중 허용” 권한을 유지합니다.

  • 그런데, 유저가 백그라운드에 조금 오래 있다가 포그라운드로 왔을 때, 다시 권한을 요청해야하는가?
    • 그럼 포그라운드에 권한 요청 코드를 넣어야하나? 그렇지 않음.
    • 첫번째 권한 요청시, 사용자가 MAT 뷰를 자신 위치로 업데이트하려고 요청했거나, 메세지에 GEO 태그를 달려고 요청했을 수 있습니다.
    • 이후 두번쨰 포그라운드 사용에서 해당 요청을 지원하기 위해서는 자동으로 팝업을 띄웁니다.
    • 핵심은, 이 사례때문에 포그라운드에 올때마다 권한을 요청하지 말라는 것 입니다

  • 때로는, 달리기를 추적하거나 탐색을 돕고 있을 수 있으며, 사용자는 백그라운드에서 일정시간 후에도 계속 액세스할 것을 기대할 수 있습니다.
    • 이는 이전 케이스와 마찬가지로, Location update를 체크하면 백그라운드에서 “사용중” 이므로 “임시 사용 중 권한”이 해제되지 않습니다. (쭉 이어진 노란 영역)

 

  • 이후 포그라운드에서 탐색을 종료하거나, 앱을 종료하거나, allowBackgroundLocationUpdates를 false로 돌리면 정상 상황으로 돌아가게 됩니다.

 

포그라운드에서만 위치를 사용하는 “한 사례”

  • 유저는 앱에게 항상 권한을 줬지만, 개발자는 유저가 직접 상호작용 할 때만 업데이트를 기대하는 경우입니다.
  • 이런 경우, allowBackgroundLocationUpdates를 false로 둔 채, 별다른 조치를 취하지 않으면 됩니다.
  • 백그라운드로 들어가면 인증이 만료되고 백그라운드 파란 표시가 표시되지 않을 것이며, 포그라운드에 돌아오면 자동으로 위치업데이트가 다시 작동됩니다.

 

새로워진 BeaconRegion

 

“항상 허용” 권한 없이, Beacon Ranging을 사용 가능해짐

  • Beacon Ranging이란?
    • iOS 7에서 Region Monitoring API의 확장 기능
    • 일반적으로 리소스를 아끼기 위해, 비콘 범위에 있는지 Monitoring으로 확인한 후 Ranging을 진행
    • 그리고 Region Monitoring는 위에서 언급했다 시피, 항상 권한이 필요했음 (iOS 12)
  • 하지만 iOS 13부터는 Region Monitoring를 위해 항상권한이 꼭 필요하지 않음
    • “사용 중 허용”권한만으로 앱이 “사용 중”일 때 작동

 

iOS 12까지의 비콘 구조

  • 인식하고 싶은 조건의 비콘들의 region을 표현하기 위해,
    • CLRegion 객체를 CLBeaconRegion 객체로 확장했고,
    • 비콘의 식별자인 uuid, major, minor이 포함
  • 그리고 이 객체를 iOS 12 Beacon Ranging API에 전달했었습니다.

  • 하지만, 비콘을 식별한다는 것에 좀더 초점을 맞춰 이야기 해보겠습니다.
    • CLBeaconRegion은 여러 비콘을 내포할 수 있는 필터링 조건이라기 보단, 그냥 큰 하나의 비콘으로 오해받을 소지가 있습니다.
    • 예를들어, minor을 비워둔다면 이는 와일드카드와 같습니다.
    • uuid와 major만 같은 비콘들의 범위를 표현하여 여러 비콘을 내포하고 있습니다. (minor, major둘다 비워두는 경우도 마찬가지)
    • 하지만 uuid와 major 만 설정된 그냥 하나의 비콘으로 오해받을 수 있는거죠

 

iOS 13에서 변경점 (CLBeaconIdentityContraint)

  • 이것이 제가 CLBeaconIdentityContraint를 추가한 이유입니다.
  • CLBeaconRegion은 비콘 식별자를 가지는게 아닌, 식별 제약조건을 가지는 것이므로 좀더 명확하게 구분할 수 있습니다.

  • beaconRegion을 생성할 때 contraint를 넣을 수 있고,
  • let beaconRegion = CLBeaconRegion(beaconIdentityContraint: contraint, identifier: myBeaconRegion)
  • startRagingBeacon을 호출할 때, beaconRegion에서 contraint 인스턴스를 가져와서 사용할 수 있습니다. 
  • open func startRangingBeacons(satisfying constraint: CLBeaconIdentityConstraint)

 

박물관 예시 (코드와 이미지까지 다 있음)

간단한 예를 통해 어떻게 작동하는지 살펴보겠습니다.

  • 박물관 어플에서, 작품에 다가가면 세부정보를 자동으로 제공하고 싶습니다.
  • 이를 위해서 비콘들을 설치할 것이고,
  • 박물관의 모든 비콘은 uuid가 같고, 각 전시실마다 major, 작품마다 minor 번호를 부여합니다.
  • 요구조건은
    • 방문자가 전시실 중 하나에 있는지 없는지 판단할 수 있어야 합니다.
    • 카페테리아에 있을 때는, ranging을 멈추고 싶습니다.
    • 전시실 중 하나에 있다면, beacon raging을 통해 방문자가 가장 가까운 객체가 무엇인지 확인해야합니다.
    • 마지막으로 이 모든 것은 앱을 “사용중” 일 때 발생합니다.

  • 박물관 가이드이기에, 앱을 사용중이라고 안전하게 가정할 수 있습니다.
    • 이를 상기시키고 싶다면, 박물관에 도착했을 때 로컬 알림을 트리거할 수 있습니다.
  • 그럼 앱이 어떤 구조일지 다이어그램을 보여드리겠습니다.

  • 기본적으로 앱 사용시 비콘 영역을 모니터링합니다.
  • Region을 들어오고 나가는 것에 반응하여 Raging을 시작하여 작품과의 거리를 측정합니다.
  • 첫번째 상태인 Monitor for BeaconRegion에 집중해 보겠습니다.
    • BeaconRegion을 어떻게 정의할까요?
    • 방문자가 어떤 전시실에든 들어오는 시점에 관심이 있으므로, uuid만을 사용하여 비콘을 모니터링 합니다.
    • 코드를 보자면 아래와 같습니다.
    // 사용 시 허용 권한이 있는지 확인
    self.locationManager.requestWhenInUseAuthorization()
    // 박물관 uuid로 모든 비콘을 모니터링
    let constraint = CLBeaconIdentityConstraint(uuid: museumUUID)
    let beaconRegion = CLBeaconRegion(beaconIdentityContraint: contraint,
                                                   identifier: museumUUID.uuidString)
    self.locationManager.startMonitoring(for: beaconRegion)
    
    • 이 시점에서, 전시실 안에 유저가 있는지 없는지 판단할 수 있습니다.
    • 이제 대리자 메소드에서 상태 변경에 반응하면 됩니다.
    // Region에 인/아웃시 호출
    func locationManager(_ manager: CLLocationManager,
           didDetermineState state: CLRegionState,
                        for region: CLRegion) {
        let beaconRegion = region as? CLBeaconRegion
        if state == .inside {
            // 전시실에 들어왔으니 비콘 Ranging 시작
            manager.startRangingBeacons(satisfying: beaconRegion!.beaconIdentifyConstraint)    
        } else {
            // 카페테리아를 가든, 박물관에서 나가든 전시실에서 나갔으므로 비콘 Ranging 중지
    	      manager.stopRangingBeacons(satisfying: beaconRegion!.beaconIdentifyConstraint)    
        }
    }
    
    • 보시다시피 region의 beaconIdentifyConstraint를 통해, 모니터링을 시작할 수 있습니다
    • 이 대리자 메소드는, 모니터링을 처음 시작할 때에도 호출되어 초기상태를 알 수 있습니다.
    • 방문자가 전시실 내부에서 앱을 시작했다면, inside로 메소드가 호출됩니다.
  • 이제 내부에서 Beacons Found시 어떤 작품이 가장 가까운지 판단해보겠습니다.
    • 대리자 메소드만 구현하면 됩니다
    // 비콘의 근접성이 업데이트될 때 호출
    func locationManager(_ manager: CLLocationManager,
                  didRange beacons: [CLBeacon],
       satisfying beaconConstraint: CLBeaconIdentityConstraint) {
        // 근접성에 따라 비콘들을 분류
        for range in [CLProximity.immediate, .near, .far, .unknown] {
            let proximityBeacons = beacons.filter { $0.proximity == range }
            // TODO: 발견한 비콘에 해당하는 작품 컨텐츠 노출
        }
    }
    
  • 유저는 아마 전날이나 며칠전에 우리 앱을 다운로드 했을 것입니다.
    • 로컬 알림을 사용하여, 박물관에 도착했을 때 우리 앱을 실행하도록 상기시키고 싶습니다.
    • 아래 코드는 location에 의해 트리거 됩니다
    let center = CLLocationCoordinate2D(latitude: 37.335400, longtitude: -122.009201)
    let region = CLCircularRegion(center: center, radius: 2000.0, identifier: "Museum")
    region.notifyOnEntry = true
    region.notifyOnExit = false // 나갈때는 필요 없으므로
    let tigger = UNLocationNotificationTrigger(region: region, repeats: false)
    

 

 

마지막 최종 요약

  • 위치 권한 부여가 변경되었습니다.
    • 앱에 더 잘 맞고, 사용자의 개인정보가 신중하게 보호되고 있다는 사실을 알려줌으로써, 앱을 통해 사용자와 상호작용할 때 자신감을 높이는 것에 도움이 되었길 바랍니다. (ㅋㅋ;)
    • 사용 중 임시권한, 항상 임시권한
  • 새로운 범위 지정
  • (요약에서 별 얘기를 안함)
Comments