CMMotion​Activity

사람은 자신이 어떤 행동을 하고 있는지 시각계, 자기수용, 전정계 등에서 전달되는 감각적인 정보를 통해 인지합니다. 그 감각들 중에서도 가장 우선시 되는 것은 전정계로, 전정계는 방향의 변화를 감지하는 반고리관과 수평과 수직에 예민한 이석 으로 구성되어 있습니다.

오늘날의 iPhone은 카메라, 기압계, 자이로스코프, 자력계 그리고 가속계를 모두 포함하고 있습니다. 이는 사람처럼 감각적인 정보의 변화를 인지하고 기기의 위치(position)와 방향(orientation)을 알 수 있으며 때론 우리의 생체 역학적인 과정과 비슷할 정도입니다.

감각적인 입력을 이해한다는 것은 그 주체가 누구냐에 상관없이 어려운 일입니다. 결정하기엔 너무나도 많은 정보들이 존재하고 있기 때문이죠. (젠장, 우리 종족은 이 결정을 올바르게 내리기까지 수백만년이 걸렸고 승강기나 비행기 또는 롤러 코스터같은 발명품 앞에서는 여전히 혼란스럽다구요!)

몇 번에 걸친 OS와 하드웨어 업그레이드를 통해 Apple의 기기는 다른 방식의 운동을 민첩하게 구별할 수 있게 되었습니다. 그래서 직접 구현할 필요도 없는 데다가 오늘의 주제가 내장된 API에 대해 토론하는 것입니다!


iOS와 watchOS에선 CMMotionActivityManager 가 가공되지 않은 센서 데이터를 기기에서 받으며 여러분이 움직이고 있는지, 움직이고 있다면 걷고 있는지 달리고 있는지 자전거를 타는지 또는 운전중인지를 알려줍니다.

이 API를 사용하려면 액티비티 매니저를 생성하고 startActivityUpdates 메소드를 사용해서 업데이트가 있는지 들을 수 있게 해야합니다. 기기가 모션 액티비티를 업데이트할 때마다 startActivityUpdates는 CMMotionActivity 객체를 전달하고 정해진 클로저를 실행합니다.

let manager = CMMotionActivityManager()
manager.startActivityUpdates(to: .main) { (activity) in
    guard let activity = activity else {
        return
    }

    var modes: Set<String> = []
    if activity.walking {
        modes.insert("🚶‍")
    }

    if activity.running {
        modes.insert("🏃‍")
    }

    if activity.cycling {
        modes.insert("🚴‍")
    }

    if activity.automotive {
        modes.insert("🚗")
    }

    print(modes.joined(separator: ", "))
}

CMMotionActivityManager 는 Core Motion 프레임워크에 있는 기능입니다. Core Motion을 지원하는 기기라면 모두 모션 보조 프로세서(coprocessor)를 장착하고 있는데, 이 보조 프로세서 덕분에 모든 센서의 처리과정이 CPU에서 일어나는 일을 막고 기기의 전체적인 에너지 사용량을 최소화할 수 있게 되었습니다.

M-시리즈 에 보조 프로세서가 합쳐진 것은 아이폰 5S의 M7칩이 첫번째였습니다. 그와 동시에 iOS 7과 Core Motion API가 발표되었습니다.

운전자에 특화된 기능이 있습니다

아마도 모션 액티비티가 가장 잘 사용된 곳은 iOS 11에 추가된 “운전 중 방해 금지 모드”일 것입니다. 이 기능이 소개된 이후로 자동차에 있는 것을 감지하는게 훨씬 좋아졌습니다.

낮은 속도에선 가속계에서 오는 정보 하나만으론 자동차를 타고있다고 결정하기가 어려웠습니다. 아마도 iPhone은 이 기능에 기기의 나침반에 있는 자력계 데이터를 사용하는 것으로 보입니다. 왜냐하면 보통의 차량은 금속에 둘러싸여있고 전자기 흐름이 감소될 것이기 때문입니다.

이 기능을 통해 안전 문제를 해결하는 것을 넘어서 어떤 앱들은 화면을 사용자의 상태에 따라 바꾸기도 할 것입니다. 예를 들어 배달 서비스 앱의 경우엔 배달원의 모션 액티비티의 변화에 기반해서 배달 예상 시간을 계산하거나 매장에서 픽업했다면 이를 고객에게 알릴 수도 있을 것입니다.

아무 행동없이 움직이는 것은 어떤 상태일까요?

CMMotionActivity 는 서로 다른 종류의 모션에 대한 Boolean 속성을 가지고 있으며 심지어 움직임에 변화가 있는지를 알려주는 값도 존재합니다. 논리적으로 생각해보면 걷는 것과 운전하는 것을 동시에 할 수 없는데 이런 값이 있는 것은 직관적이지 않아 보일 수도 있습니다.

이 부분은 CMMotionActivity 문서에 명확하게 잘 설명돼 있습니다.

이 클래스의 모션 관련 속성은 서로 배타적이지 않아서 하나 이상의 모션 관련 속성이 true 값을 가질 수 있습니다. 예를 들면 사용자가 자동차를 운전하고 있고 자동차가 빨간 불에 멈췄다면 그 변화에 관련된 이벤트의 모션 관련 속성은 cyclingstationarytrue 로 올 것입니다.

다음은 여러 상황에서 API가 어떻게 작동하는지에 대한 예제입니다.

시나리오 1 차 안에 있는데 빨간 불이라서 멈췄을 때

🚶‍ walking 🏃‍ running 🚴‍cycling 🚗 automotive 🛑 stationary
false false false true true

시나리오 2 움직이는 차량안에 있을 때

🚶‍ walking 🏃‍ running 🚴‍cycling 🚗 automotive 🛑 stationary
false false false true false

시나리오 3 기기는 움직이고 있는데 나는 걷지도 운전하고 있지도 않을 때

🚶‍ walking 🏃‍ running 🚴‍cycling 🚗 automotive 🛑 stationary
false false false false false

시나리오 4 당신은 용의자를 추적중인 세계에서 제일 유명한 탐정이고 지금은 움직이는 기차의 복도를 따라 움직이다가 마지막 칸에 도착해서 용의자가 어디에 숨었을 지 찾느라 멈췄습니다. (어디 구석에 사람 크기의 박스가 있지 않을까요?)

🚶‍ walking 🏃‍ running 🚴‍cycling 🚗 automotive 🛑 stationary 🕵️‍🇧🇪 에르퀼 푸아로
false true false true true true

(마지막 시나리오는 정말 이렇게 될 지 확신이 없네요…)

그 문서가 말해주는 전반적인 지침은 stationary 속성을 CMMotionActivity의 다른 속성들과는 같이 사용할 수 있도록(직교할 수 있도록) 취급해야 한다는 것입니다. 그렇게 한다면 다른 모든 조합들에 대비할 수 있다고 합니다.

또한 각각의 CMMotionActivity 객체에는 .low, .medium, 그리고 .high 값을 가지는 confidence 속성이 있습니다. 불행히도 문서에는 이 값들이 어떤 것이며 어떻게 쓰여야 하는 지에 대한 설명이 명확하게 적혀있지 않습니다. 종종 그래왔듯이 경험적인 접근 방식이 필요합니다. 여러분의 앱에 값을 다르게 적용해서 사용해보면서 어떤 값이 적절한 결과를 반환하는지 알아내야 할 것입니다.

위치 정보와 조합해서 사용하기

사용 방식에 따라 Core Location 데이터와 Core Motion을 동시에 사용하는 것은 괜찮은 결과를 낼 수도 있습니다.

시간별 위치 변화에 낮은 신뢰도의 모션 액티비티를 추가하는 것만으로도 정확도를 높일 수 있습니다. 다음은 운송 수단의 종류별 속도 범위 가이드라인입니다.

  • 걷는 속도는 보통 최대 초당 2.5미터입니다. (5.6 mph, 9 km/h)
  • 달리는 속도는 초당 2.5미터에서 7.5미터입니다. (5.6 ~ 16.8 mph, 9 ~ 27 km/h)
  • 자전거의 속도는 초당 3미터에서 12미터입니다. (6.7 - 26.8 mph, 10.8 ~ 43.2 km/h)
  • 차량의 속도는 초당 100미터도 넘을 수 있습니다. (220 mph, 360 km/h)

또는 위치 정보를 사용해서 지금 물 속에 있는지를 알아내서 UI를 변경할 수도 있습니다.

if currentLocation.intersects(waterRegion) {
    if activity.walking {
        print("🏊‍")
    } else if activity.automotive {
        print("🚢")
    }
}

하지만 위치 데이터는 큰 변화가 있을 때처럼 정말로 필요할때만 사용해야 합니다. 왜냐하면 위치 데이터는 GPS부터 필요하면 셀룰러까지 켜야하기 때문에 에너지를 많이 잡아먹습니다.


CMMotionActivityManager 는 Core Motion을 대표하는 훌륭한 API 중 하나이며 덕분에 우리는 이 API를 통해 몰입력과 반응성이 좋은 앱을 만들 수 있게 되었습니다.

지금까지 여러분의 앱에 모션을 적용하지 않았거나 Core Motion을 고려해본 적이 없으시다면 이 프레임워크의 가능성에 깜짝 놀라실 것입니다!

다음 글

우리 NSHipster는 변경된 내용은 적어도 우리의 매일에 큰 영향을 주는 그런 핵심적인 기술을 좋아합니다. 오늘은 iOS 12의 릴리즈를 축하하는 의미로 iOS 11.4와 12의 API 차이에 대해 알아낸 것들을 공유하고자 합니다.