App Programming Guide for iOS - Background Execution (3)

Background Execution

사용자가 앱을 적극적으로 사용하지 않을 때, 시스템은 앱을 백그라운드 상태로 이동시킨다. 많은 앱의 경우 백그라운드 상태는 앱이 일시 중지되기 전 잠시 들리는 곳이다. 앱을 일시 중지 시키는 것은 배터리 수명을 향상시켜 사용자의 주의를 끈 새로운 포그라운드 앱에게 중요한 시스템 자원을 사용하게 한다.

대부분의 앱은 일시 중지 상태로 쉽게 이동하지만 앱이 백그라운드에서 계속 동작할 수 있는 몇 가지 정당한 사유가 존재한다. 하이킹 앱은 시간 경과에 따라 유저의 위치를 추적하여 해당 코스가 하이킹 지도 위에 나타나도록 할 수 있다. 오디오 앱은 잠금 화면에서 음악을 계속 재생해야 할 수 있다. 또 다른 앱은 유저에게 컨텐츠를 보이기까지의 지연을 최소화하고자 백그라운드에서 컨텐츠를 다운로드 할 수도 있다. 만약 당신의 앱이 백그라운드에서 동작하는 것이 필요하다면, iOS는 효율적이고 시스템 자원이나 유저의 배터리를 소모하지 않고 할 수 있도록 도와준다. iOS가 제공하는 기술은 세 가지 카테고리로 나눠진다.

  • 포그라운드에서 짧은 작업을 시작한 앱은 앱이 백그라운드 상태로 이동했을 때 그 작업을 완료할 시간을 요청할 수 있다.
  • 포그라운드에서 다운로드를 시작한 앱은 시스템에게 그 다운로드에 대한 관리를 넘김으로써 다운로드가 지속되는 동안 앱이 일시 중지되거나 종료될 수 있도록 한다.
  • 특정 유형의 작업을 지원하기 위해 백그라운드에서 동작해야 하는 특정 작업을 실행하는 앱은 하나 이상의 백그라운드 실행 모드에 대한 지원을 선언할 수 있다.

그렇게 하지 않으면 전반적인 사용자 경험이 향상되지 않는 한 항상 백그라운드 작업은 삼가라. 유저가 다른 앱을 실행하거나 기기를 잠그고 지금 당장 사용하지 않는다는 이유로 앱은 백그라운드 상태로 이동할 수 있다. 두 경우 모두, 유저는 당신의 앱이 지금 의미있는 작업을 할 필요가 없다고 신호를 보내는 것이다. 그러한 조건 하에서 실행을 지속하는 것은 기기의 배터리를 소모할 뿐이고 사용자가 앱을 강제 종료시키게 만들지도 모른다. 그래서 백그라운드에서 할 작업에 대해 유의하고 가능하다면 피하라.

Executing Finite-Length Tasks

백그라운드 모드로 이동하는 앱은 시스템에 의해 일시 중지될 수 있도록 가능한 한 빨리 그 자신들이 조용한 상태가 될 것으로 예상한다. 만약 앱이 작업 도중이고 그 작업을 완료하기 위해 약간의 시간이 더 필요하다면, UIApplication 객체의 beginBackgroundTaskWithName:expirationHandler:beginBackgroundTaskWithExpirationHandler:를 실행하여 추가 실행 시간을 요구하라. 이들 메서드 둘 중 하나를 호출하는 것은 앱의 작업을 완료하도록 약간의 시간을 줌으로써 앱의 종료를 일시적으로 지연시킨다. 작업이 끝나면 앱은 endBackgroundTask: 메서드를 호출해 시스템에게 작업이 끝났다는 것과 일시 중지될 수 있음을 알린다.

beginBackgroundTaskWithName:expirationHandler:beginBackgroundTaskWithExpirationHandler: 메서드를 호출할 때마다 해당 작업과 관련된 고유한 토큰을 생성한다. 앱이 작업을 마치면 고유한 토큰을 인자로 갖는 endBackgroundTask:를 호출하여 시스템이 그 작업이 끝났음을 알게 한다. 백그라운드 작업에 대한 endBackgroundTask:를 호출하지 않으면 앱이 종료가 된다. 작업 시작시 만료 핸들러를 제공한다면 시스템이 그 핸들러를 호출하고 작업을 완료하고 종료를 피하는 마지막 기회를 준다.

백그라운드 작업을 지정하기 위해 앱이 백그라운드 모드로 이동하길 기다릴 필요가 없다. 더 나은 방법은 작업 시작 전 beginBackgroundTaskWithName:expirationHandler:beginBackgroundTaskWithExpirationHandler:를 호출하고 마치자마자 endBackgroundTask:를 호출하는 것이다. 앱이 포그라운드에서 동작할 때도 이 패턴을 따를 수 있다.

Listing 3-1은 앱이 백그라운드로 전환되었을 때 장기 실행 작업을 어떻게 시작하는 지 보여준다. 이 예제에서 백그라운드 작업 시작 요청에는 작업이 너무 오래 걸리는 경우에 대비해 만료 핸들러가 포함된다. 작업은 dispatch 큐에 넣어져 applicationDidEnterBackground: 메서드가 바로 반환되도록 비동기적으로 실행된다. ‘블록’(클로저)의 사용은 백그라운드 작업 식별자 같은 중요한 변수에 대한 참조를 유지하는 데 필요한 코드를 단순화시킨다. bgTask 변수는 클래스의 멤버 변수로 현재 백그라운드 작업의 식별자를 가리키고 메서드에서 사용되기 전 초기화된다.

Listing 3-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func applicationDidEnterBackground(_ application: UIApplication) {
bgTask = application.beginBackgroundTask(withName: "MyTask") { [weak self] in
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.

guard let strongSelf = self,
let bgTask = strongSelf.bgTask else { return }

application.endBackgroundTask(bgTask)
strongSelf.bgTask = UIBackgroundTaskInvalid
}

DispatchQueue.global().async { [weak self] in
guard let strongSelf = self,
let bgTask = strongSelf.bgTask else { return }

// Do the work associated with the task, preferably in chunks.

application.endBackgroundTask(bgTask)
strongSelf.bgTask = UIBackgroundTaskInvalid
}
}

(가이드에 있던 Objective-C 코드를 Swift로 바꿔보았습니다.)

작업을 시작할 때 항상 만료 핸들러를 제공하라. 하지만 앱의 남은 실행 시간을 알고 싶다면 UIApplication의 backgroundTimeRemaining의 값을 사용하라.

당신만의 만료 핸들러 안에서 작업을 종료하는 데 필요한 추가 코드를 포함시킬 수 있다. 그러나 만료 핸들러가 호출될 때는 앱의 종료가 임박했기 때문에 추가하는 코드는 실행하는 데 오래걸리면 안 된다. 이러한 이유 때문에 상태 정보의 최소 정리만을 수행하고 작업을 끝내야 한다.

Downloading Content in the Background

파일을 다운로드 할 때는 NSURLSession 객체를 사용하여 앱이 일시 중지되거나 종료될 때를 고려하여 시스템이 다운로드 과정을 관리하도록 하는 것이 좋다. 백그라운드 전송을 위한 NSURLSession 객체를 구성하면 시스템은 그 전송들을 각각의 프로세스로 관리하고 정상적인 방법으로 상태를 다시 앱에 보고한다. 전송이 진행되는 동안 앱이 종료되면 시스템은 백그라운드에서 전송을 계속하고 전송이 완료되거나 하나 이상의 작업에 앱의 주의가 필요할 경우 앱을 시작한다.

백그라운드 전송을 지원하기 위해 당신은 NSURLSession 객체를 적절히 구성해야 한다. 세션을 구성하기 위해 NSURLSessionConfiguration 객체를 만들고 여러 속성을 적절한 값으로 설정한다. 그 다음 만들어진 설정 객체를 NSURLSession의 초기화 메서드에 전달한다.

백그라운드 다운로드를 지원하는 구성 객체를 만드는 과정은 다음과 같다.

  1. NSURLSessionConfigurationbackgroundSessionConfigurationWithIdentifier:를 사용해 구성 객체를 만든다.
  2. 구성 객체의 sessionSendsLaunchEvents 프로퍼티를 YES로 설정한다.
  3. 앱이 포그라운드에 있을 동안 전송을 시작한다면 구성 객체의 discretionary 프로퍼티 또한 YES로 설정하는 것을 추천한다.
  4. 구성 객체의 다른 프로퍼티들도 적절히 설정한다.
  5. NSURLSession 객체의 생성에 구성 객체를 사용한다.

일단 구성되면 NSURLSession 객체는 적절한 시간에 업로드 및 다운로드 작업을 시스템에 원활하게 전달한다. 앱이 계속 실행되는 동안(포그라운드나 백그라운드) 작업이 끝났을 경우 세션 객체는 일반적인 방법으로 델리게이트에 알린다. 작업이 아직 끝나지 않았고 시스템이 앱을 종료시켰다면 시스템은 백그라운드에서 지속해서 작업을 관리한다. 사용자가 앱을 종료했을 경우 시스템은 남아 있는 작업을 취소한다.

백그라운드 세션과 관련된 모든 작업이 완료되면 시스템은 종료된 앱을 재시작하고(sessionSendsLaunchEvents 프로퍼티가 YES로 설정되었고 사용자가 앱을 강제 종료하지 않았다고 가정) 앱 델리게이트의 application:handleEventsForBackgroundURLSession:completionHandler: 메서드를 호출한다.(시스템은 또한 인증 문제나 앱의 주의가 필요한 작업을 수행하기 위해 앱을 재시작할 수 있다.) 델리게이트 메서드 구현에서 제공된 식별자를 이용해 전과 같은 구성으로 NSURLSessionConfigurationNSURLSession의 새 객체를 만든다. 시스템은 새 세션 객체를 이전 작업과 다시 연결하고 해당 상태를 세션 객체의 델리게이트에 보고한다.

Implementing Long-Running Tasks

구현하기 위해 더 많은 시간이 요구되는 작업의 경우 일시 중지되지 않고 백그라운드에서 실행할 수 있도록 특정 권한을 요청해야 한다. iOS에서는 특정 앱 유형만 백그라운드에서 실행할 수 있다.

  • 음악 플레이어 같은 백그라운드에서 들리는 콘텐츠를 재생하는 앱
  • 백그라운드에서 녹음을 하는 앱
  • 네비게이션 앱 같이 사용자에게 항상 내 위치를 알려주는 앱
  • VoIP(Voice over Internet Protocol)를 지원하는 앱
  • 새 콘텐츠를 다운로드하고 처리해야 하는 앱
  • 바깥 액세서리에서 정기적인 업데이트를 받는 앱

이러한 서비스를 구현하는 앱은 지원하는 서비스와 그 서비스의 관련 측면을 구현하기 위해 사용하는 시스템 프레임워크를 선언해야 한다. 서비스를 선언하는 것은 앱이 어떤 서비스를 사용하는 지 시스템이 알게끔 하지만 어떤 경우에는 실제로 앱이 일시 중단되는 것을 막는 시스템 프레임 워크이다.

Declaring Your App’s Supported Background Tasks

일부 유형의 백그라운드 실행에 대한 지원은 이를 사용하는 앱에 의해 미리 선언되어야 한다. Xcode 5 이상에서는 앱이 지원하는 백그라운드 모드를 프로젝트 세팅의 Capabilities 탭에서 선언한다. 백그라운드 모드를 가능하게 하는 것은 Info.plist에 UIBackgroundModes 키를 추가하는 것이다. 하나 이상의 체크 박스를 선택하면 해당 백그라운드 모드 값이 해당 키에 추가된다. 표 3-1은 Xcode가 Info.plist 파일의 UIBackgroundModes 키에 할당하는 백그라운드 모드 값들이다.

Xcode 백그라운드 모드
UIBackgroundModes value
설명
Audio and AirPlay audio 앱은 백그라운드에서 유저에게 컨텐츠를 들려주거나 녹음할 수 있다.(이 콘텐츠는 스트리밍 에어플레이를 사용하는 오디오나 비디오 컨텐츠를 포함한다.) 사용자는 앱이 마이크를 사용할 수 있는 권한을 주어야 한다. 더 많은 정보는 Supporting User Privacy를 참고하라.
Location updates location 앱이 백그라운드에서 실행 중일 때도 사용자에게 위치 정보를 제공한다.
Voice over IP voip 사용자가 인터넷 연결을 사용해 전화를 걸 수 있도록 제공한다.
Newsstand downloads newsstand-content 백그라운드에서 잡지나 신문 컨텐츠를 다운로드하고 처리하는 Newsstand 앱이다.
External accessory communication external-accessory 이 앱은 External Accessory 프레임워크를 통해 정기적으로 업데이트를 전달하는 하드웨어 액세서리와 함께 동작한다.
Uses Bluetooth LE accessories bluetooth-central 이 앱은 Core Bluetooth 프레임워크를 통해 정기적인 업데이트를 전달하는 블루투스 액세서리와 함께 동작한다.
Acts as a Bluetooth LE accessory bluetooth-peripheral 이 앱은 Core Bluetooth 프레임 워크를 통해 주변 장치 모드에서 블루 통신 모드를 지원한다. 이 모드는 유저의 권한 설정이 필요하며 Supporting User Privacy를 참고하라.
Background fetch fetch 이 앱은 네트워크로부터 소량의 컨텐츠를 다운로드하고 처리한다.
Remote notifications remote-notification 이 앱은 푸시 알림이 도착하면 컨텐츠를 다운로드하길 원한다. 푸시 알림과 관련된 컨텐츠 표시의 지연을 최소화하기 위해 이 알림을 사용하라.

위의 각 모드들은 관련 이벤트에 응답하기 위해 적절한 시기에 앱이 깨어나야 하거나 시작되어야 함을 시스템에게 알게 한다. 예를 들어 음악 재생을 시작하고 백그라운드로 이동하는 앱은 오디오 출력 버퍼를 채우기 위해 실행 시간이 더 필요하다. 오디오 모드를 활성화하면 적절한 간격으로 앱에 필요한 콜백을 계속해서 수행해야 한다는 것을 시스템 프레임 워크에 알려준다. 만약 앱이 이 모드를 선택하지 않으면, 앱에 의해 재생되거나 녹음되는 오디오는 앱이 백그라운드로 갔을 때 정지한다.

Tracking the User’s Location

백그라운드에서 유저의 위치를 추적하는 몇가지 방법이 있다. 대부분의 경우 앱이 백그라운드에서 계속해서 실행될 필요는 없다.

  • 중요한 위치 변경 서비스(권장됨)
  • 포그라운드 전용 위치 서비스
  • 백그라운드 위치 서비스

중요한 위치 변경 서비스는 정확한 위치 데이터가 필요하지 않은 앱에 적합하다. 이러한 서비스에서는 유저의 중요한 위치 변경이 일어났을 때에만 위치 갱신이 일어난다. 그래서 소셜 앱이나 그리 중요하지 않은 위치 관련 정보를 제공하는 앱에게 적합하다. 만약 갱신이 일어날 때 앱이 일시 중지한다면, 시스템이 앱을 백그라운드에서 깨워 갱신을 처리하게 한다. 만약 앱이 이 서비스를 시작한 후 종료된다면, 새로운 위치가 사용 가능할 때 앱을 재시작한다. 이 서비스는 iOS 4 이상부터 사용 가능하며 celluar radio가 있는 기기에서만 가능하다.

포그라운드 전용과 백그라운드 위치 서비스는 위치 데이터를 받기 위해 표준 Core Location 서비스를 사용한다. 유일한 다른 점은 포그라운드 전용 위치 서비스는 앱이 다른 백그라운드 서비스나 작업을 지원하지 않는 경우 발생할 수 있는 일시중지 상태에서 위치 갱신의 전달을 중단한다는 것이다. 포그라운드 전용 위치 서비스는 포그라운드일 때만 위치 정보가 필요한 앱을 대상으로 한다.

Xcode 프로젝트의 Capabilities 탭에서 백그라운드 모드의 위치 지원을 활성화 할 수 있다.(Info.plist 파일에서 UIBackgroundModes 키를 location 값과 함께 포함시켜도 가능하다.) 이 모드를 활성화하는 것이 시스템이 앱을 일시 중지시키는 것을 방지하지는 않지만 전달할 새로운 위치 데이터가 있을 때마다 시스템에게 앱을 깨워야 한다고 알려준다. 그래서 이 키는 효과적으로 백그라운드에서 동작하는 앱이 위치 갱신이 일어날 때마다 그것을 처리하도록 한다.

표준 서비스를 조금씩 사용하거나 중요한 위치 변경 서비스를 대신 사용하는 것을 권장한다. 위치 서비스는 iOS 기기의 온보드 무선 하드웨어의 적극적 사용을 요구한다. 이 하드웨어를 지속적으로 사용하는 것은 많은 전력을 소비하게 한다. 만약 앱이 유저에게 정확하고 지속적인 위치 정보를 제공할 필요가 없다면 위치 서비스의 사용을 최소화하는 것이 좋다.

앱에서 각각 다른 위치 서비스를 어떻게 이용하는 지 더 알고 싶으면 Location and Maps Programming Guide를 참고하라.

Playing and Recording Background Audio

지속적으로(심지어 앱이 백그라운드에서 실행되고 있을 때도) 오디오를 재생하거나 녹음하는 앱은 그러한 작업을 백그라운드에서 동작하도록 등록할 수 있다. Xcode 프로젝트의 Capabilities 탭의 Background 모드에서 오디오 지원을 활성화 할 수 있다.(Info.plist 파일에서 UIBackgroundModes 키와 audio 값을 포함시켜도 활성화가 가능하다.) 백그라운드에서 오디오 컨텐츠를 재생하는 앱은 조용해지지 않으면서 컨텐츠를 재생해야 한다.

백그라운드 오디오 앱의 전형적인 예는 다음과 같다.

  • 음악 재생 앱
  • 오디오 녹음 앱
  • AirPlay를 통한 오디오 또는 영상 재생 앱
  • VoIP 앱

UIBackgroundModes 키가 audio 값을 가지게 되면, 시스템의 미디어 프레임워크는 자동적으로 해당 앱이 백그라운드로 이동해도 일시 중지되지 않도록 한다. 오디오, 영상 컨텐츠를 재생 또는 오디오 컨텐츠를 녹음하는 한 앱은 백그라운드에서 계속 실행된다. 그러나 녹음이나 재생이 멈춘다면, 시스템은 앱을 일시 중지한다.

임의의 시스템 오디오 프레임워크를 사용해 백그라운드 오디오 컨텐츠를 작업할 수 있고 그 프레임워크를 사용하는 과정은 변하지 않는다. (AirPlay를 통한 영상 재생의 경우, Media Player나 AVFoundation 프레임워크를 사용해 영상을 보여줄 수 있다.) 앱은 미디어 파일을 재생할 때 일시 중지되지 않기 때문에 앱이 백그라운드에 있을 때에 콜백이 정상적으로 동작한다. 그럼에도 불구하고 콜백에서는 재생을 위한 데이터 제공에 필요한 작업만을 수행하는 것이 좋다. 예를 들어, 스트리밍 오디오 앱은 서버로부터 음악 스트림 데이터를 다운로드할 필요가 있고 재생을 위해 현재 오디오 샘플을 푸시해야 한다. 앱은 재생과 관련되지 않은 작업을 해서는 안 된다.

둘 이상의 앱이 오디오를 지원할 수도 있기 때문에 시스템은 주어진 시간에 어떤 앱이 오디오를 재생하고 녹음할 지의 허용 여부를 결정한다. 포그라운드 앱은 오디오 작업에 대해 항상 우선권을 갖는다. 둘 이상의 백그라운드 앱이 오디오 재생을 허용하도록 할 수도 있는데 그러한 결정은 각 앱의 오디오 세션 객체의 설정에 따라 이루어진다. 항상 앱의 오디오 세션 설정을 적절하게 해야 하며 인터럽션이나 오디오 관련 알림에 대한 처리를 다루기 위해 시스템 프레임워크와 함께 신중하게 작업해야 한다. 백그라운드 실행을 위한 오디오 세션 객체 설정에 대해 더 알고 싶다면, Audio Session Programming Guide를 참고하라.

Implementing a VoIP App

Voice over Internet Protocol(VoIP) 앱은 유저가 기기의 셀룰러 서비스 대신 인터넷 연결을 사용해 전화를 걸 수 있게 한다. 이러한 앱은 관련 서비스와의 지속적인 네트워크 연결이 필요한데 이를 통해 전화 수신과 관련 데이터를 받을 수 있다. VoIP 앱을 항상 깨우기보다는 시스템은 앱을 일시 중지시켜 놓고 소켓을 모니터링 할 수 있는 기능을 제공한다. 들어오는 트래픽이 감지되면 시스템은 VoIP 앱을 깨우고 소켓에 대한 제어권을 돌려준다.

VoIP 앱을 구성하려면 다음을 수행해야 한다.

  1. Xcode 프로젝트 안 Capabilities 섹션의 Background 모드에서 Voice over IP 지원을 활성화하라.(Info.plist 파일에서 UIBackgroundModes 키에 voip 값을 추가하는 것으로도 활성화시킬 수 있다.)
  2. VoIP의 사용을 위한 앱의 소켓을 구성하라.
  3. 백그라운드 이동 전에 setKeepAliveTimeout:handler:를 주기적으로 실행될 핸들러를 설치하기 위해 호출하라. 앱은 이 핸들러를 사용해 서비스 연결을 유지한다.
  4. 오디오 세션을 구성하여 활성 사용과의 전환을 처리하라.

UIBackgroundModes 키에 voip 값을 포함시키는 것은 네트워크 소켓을 관리하기 위해 앱이 백그라운드에서 실행되는 것을 허용해야 함을 시스템에게 알려준다. 이 키가 있는 앱은 시스템 부팅 직후 백그라운드에서 즉시 시작되어 VoIP 서비스를 항상 사용할 수 있다.

대부분의 VoIP 앱은 또한 백그라운드 상태에서 오디오를 전달할 수 있도록 백그라운드 오디오앱으로 구성될 필요가 있다. 그래서 UIBackgroundModes 키에 audiovoip 값 두 개를 포함시켜야 한다. 만약 이것을 하지 않을 경우, 앱이 백그라운드 상태에 있을 때 오디오를 재생하거나 녹음하지 못한다. UIBackgroundModes 키와 관련된 더 많은 정보는 Information Property List Key Reference를 참고하라.

VoIP 앱을 구성하는 데 꼭 필요한 과정에 대한 특정 정보는 Tips for Developing a VoIP App을 참고하라.

Fetching Small Amounts of Content Opportunistically

주기적으로 새로운 컨텐츠를 체크해야 할 필요가 있는 앱은 컨텐츠를 받는 작업을 시작할 수 있도록 시스템에게 깨워 달라고 요청할 수 있다. 이 모드를 지원하려면 Xcode 프로젝트 안 Capabilities 탭의 Background 모드 섹션에서 Background fetch 옵션을 활성화하라.(Info.plist 파일에서 UIBackgroundModes 키에 fetch 값을 포함시켜도 가능하다.) 이 모드를 활성화하는 것이 시스템이 앱이 언제든지 백그라운드에서 가져 오기를 수행할 수 있음을 보장하지는 않는다. 시스템은 컨텐츠를 가져오는 앱의 필요와 다른 앱이나 시스템 자체의 필요의 균형을 맞춰야 한다. 그 정보를 평가한 후, 그 작업을 할 좋은 기회가 있을 때 시스템이 앱에 시간을 준다.

좋은 기회가 왔을 때, 시스템은 앱을 백그라운드에서 깨우거나 재시작하고 앱 델리게이트의 application:performFetchWithCompletionHandler: 메서드를 호출한다. 이 메서드를 사용해 새로운 컨텐츠를 체크하고 컨텐츠를 사용 가능한 경우 다운로드 작업을 시작하라. 새로운 컨텐츠의 다운로드를 완료하자마자 제공된 컴플리션 핸들러를 실행해야 하는데 이는 컨텐츠가 사용 가능한지의 여부를 알려주는 결과를 전달한다. 이 블럭(클로저)을 실행하는 것은 시스템에게 앱을 일시 중지 상태로 이동시키는 것이 가능하고 전력 사용량을 평가할 수 있음을 알려 준다. 소량의 컨텐츠를 빠르게 다운로드하고 다운로드가 가능한 컨텐츠를 정확히 반영하는 앱은 컨텐츠를 받는 데 오래 걸리는 앱이나 아무것도 다운로드하지 않지만 컨텐츠가 사용 가능하다고 주장하는 앱보다 미래에 실행 시간을 받을 확률이 높다.

임의의 컨텐츠를 다운로드 할 때, NSURLSession 클래스를 사용해 다운로드를 시작하고 관리하는 것이 권장된다. 업로드, 다운로드 작업을 관리하기 위해 이 클래스를 어떻게 사용하는 지에 대한 더 많은 정보는 URL Loading System Programming Guide를 참조하라.

Using Push Notifications to Initiate a Download

만약 앱의 새로운 컨텐츠가 사용 가능할 때 서버가 유저의 기기로 푸시 알림을 보낸다면, 바로 새로운 컨텐츠를 다운로드하는 것을 시작하도록 시스템이 앱을 백그라운드에서 실행하게끔 요청할 수 있다. 백그라운드 모드의 의도는 유저가 푸시 알림을 볼 때와 앱이 관련 컨텐츠를 표시할 수 있을 때 간의 경과 시간을 최소화하는 것이다. 앱은 대개 유저가 알림을 볼 때와 거의 동시에 일어나지만 그렇지 않은 경우보다 더 많은 시간을 제공한다.

백그라운드 모드를 지원하기 위해 Xcode 프로젝트의 Capabilities 탭에서 Background modes 섹션의 Remote notifications 옵션을 활성화하라. (Info.plist 파일에서 UIBackgroundModes 키에 remote-notification 값을 포함시키는 것으로도 활성화 할 수 있다.)

푸시 알림이 다운로드 작업을 시작하려면 알림의 페이로드가 값이 1로 설정된 content-available 키를 포함해야 한다. 그 키가 존재할 때, 시스템은 앱을 백그라운드에서 깨우고(또는 백그라운드에서 시작하고) 앱 델리게이트의 application(_:didReceiveRemoteNotification:fetchCompletionHandler:)을 호출한다. 해당 메서드의 구현은 관련 컨텐츠를 다운로드하고 앱에 그것을 통합해야 한다.

임의의 컨텐츠를 다운로드 할 때, NSURLSession 클래스를 사용해 다운로드를 시작하고 관리하는 것이 권장된다. 업로드, 다운로드 작업을 관리하기 위해 이 클래스를 어떻게 사용하는 지에 대한 더 많은 정보는 URL Loading System Programming Guide를 참조하라.

Downloading Newstand Content in the Background

새로운 잡지나 신문 발행을 다운로드하는 뉴스스탠드 앱은 그러한 다운로드를 백그라운드에서 수행하도록 등록할 수 있다. Xcode 프로젝트의 Capabilities 탭에 있는 Background modes 섹션에서 뉴스스탠드 다운로드 지원을 활성화할 수 있다. (또한 Info.plist 파일에서 UIBackgroundModes 키에 newstand-content 값을 포함시키는 것으로도 가능하다.) 이 키가 존재할 때 앱이 아직 실행중이 아닌 경우 시스템이 앱을 실행하여 새로운 발행의 다운로드를 시작할 수 있게 한다.

만약 Newsstand Kit 프레임워크를 사용해 다운로드를 시작할 경우, 시스템이 앱의 다운로드 프로세스를 처리한다. 시스템은 앱이 일시 중지 상태가 되거나 종료되었을 때도 다운로드를 이어 간다. 다운로드 작업이 완료되면 시스템은 그 파일을 앱의 샌드박스로 보내고 앱에게 알려 준다. 앱이 실행중이 아닐 경우 그 알림은 앱을 깨우고 새롭게 받은 그 파일을 처리할 수 있는 기회를 제공한다. 다운로드 과정 중 에러가 있을 경우 앱은 그것을 처리하도록 비슷하게 깨워진다.

Newsstand Kit 프레임워크를 사용해 컨텐츠를 다운로드하는 것에 대한 더 자세한 정보는 Newsstand Kit Framework Reference를 참조하라.

Communicating with an External Accessory

외부 액세서리와 함께 동작하는 앱은 앱이 일시 중지일 때 액세서리가 업데이트를 제공하면 깨워달라고 요청할 수 있다. 심장 박동 모니터와 같이 정기적인 간격으로 데이터를 제공하는 일부 유형의 액세서리에게 이러한 지원은 중요하다. Xcode 프로젝트의 Capabilities 탭에 있는 Background modes 섹션에서 외부 액세서리 지원을 활성화할 수 있다. (Info.plist 파일에서 UIBackgroundModes 키에 external-accessory 값을 포함시켜도 활성화할 수 있다.) 이 모드를 활성화하면 외부 액세서리 프레임워크가 액세서리와의 활성화된 세션을 닫지 않는다. (iOS 4 이전에는 앱이 일시 중지할 경우 이 세션은 자동으로 닫힌다.) 액세서리로부터 새로운 데이터가 도착할 경우 프레임워크는 앱을 깨워 그 데이터를 처리하도록 한다. 시스템은 또한 앱을 깨워 액세서리 연결과 연결 해제 알림을 처리하도록 깨운다.

백그라운드에서 액세서리 업데이트 처리를 지원하는 임의의 앱은 몇 가지 기본 지침을 따라야 한다.

  • 앱은 유저가 액세서리 업데이트 이벤트를 시작하거나 멈출 수 있게 허용하도록 하는 인터페이스를 제공해야 한다. 그 인터페이스는 적절하게 액세서리 세션을 열거나 닫아야 한다.
  • 깨어난 후 앱은 데이터를 처리하는 데 10초 정도가 걸린다. 이상적으로 가능한 한 빠르게 데이터를 처리해야 하고, 다시 일시 중지가 되도록 해야 한다. 그러나 만약 시간이 더 필요하다면 앱은 beginBackgroundTask(expirationHandler:) 메서드를 호출해 추가 시간을 요구할 수 있다. 하지만 꼭 필요할 때만 요청해야 한다.

Getting the User’s Attention While in the Background

알림은 일시 중지 상태, 백그라운드 상태, 또는 실행 중이 아닌 앱이 사용자의 주의를 얻을 수 있는 방법이다. 앱은 로컬 알림을 사용하여 경고를 띄우거나 소리를 내고, 앱의 아이콘에 배지를 달며 또는 이 세 가지를 조합합니다. 예를 들어, 알람 시계 앱은 로컬 알림을 통해 알람 소리를 재생하고 알람을 비활성화하는 경고를 띄웁니다. 알람이 사용자에게 전달되면 사용자는 정보가 앱을 백그라운드에서 포그라운드로 가져올지를 결정해야 한다. (만약 앱이 포그라운드에서 이미 실행중이라면 로컬 알림은 사용자에게 말고 앱에 전달된다.)

로컬 알림의 전달을 스케줄링하려면 UILocalNotification(iOS 10부터 depreated 되었고 UINotificationRequest를 대신 사용한다.) 클래스의 인스턴스를 만들고 알림의 인자를 구성하고 UIApplication 클래스의 메서드를 사용해 스케줄링하라. 로컬 알림 객체는 알림의 전달 방식(소리, 경고, 뱃지)에 대한 정보와 언제 전달될 지에 대한 정보를 담고 있다. UIApplication 클래스의 메서드는 즉시 알림을 보낼지, 정해진 시간에 알림을 보낼 지에 대한 옵션을 제공한다.

Listing 3-2는 사용자에 의해 설정된 날짜와 시간을 사용해 하나의 알람을 스케줄링하는 예시다. 이 예는 한 번에 하나의 알람을 구성하고 새로운 알람을 스케줄링하기 전에 앞에 있던 알람을 취소한다. (여러분의 앱은 임의로 주어진 시간에 128 개가 넘는 로컬 알림을 가질 수 없고 각각은 정해진 간격으로 반복되도록 구성될 수 있다.) 알람 자체는 경고 상자와 알람이 시작될 때 앱이 실행 중이지 않거나 백그라운드일 경우 재생될 소리 파일로 구성된다. 만약 앱이 활성 상태이고 그래서 포그라운드에서 실행 중일 경우 앱 델리게이트의 application:didReceiveLocalNotification 메서드가 대신 호출된다.

Listing 3-2 알람 알림 스케줄링하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)scheduleAlarmForDate:(NSDate*)theDate {
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];

// Clear out the old notification before scheduling a new one.
if ([oldNotifications count] > 0)
[app cancelAllLocalNotifications];

// Create a new notification.
UILocalNotification* alarm = [[UILocalNotification alloc] init];
if (alarm) {
alarm.fireDate = theDate;
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = 0;
alarm.soundName = @"alarmsound.caf";
alarm.alertBody = @"Time to wake up!";

[app scheduleLocalNotification:alarm];
}
}

로컬 알림에서 사용하는 소리 파일은 푸시 알림에서 사용될 것들과 같은 요구사항을 가진다. 커스텀 소리 파일은 앱의 메인 번들 안에 위치해야 하며 다음 중 하나의 포맷을 지원해야 한다: Linear PCM, MA4, µ-Law, 또는 a-Law. 또한 UILocalNotificationDefaultSoundName 상수를 지정해 기기의 기본 경고 소리를 재생할 수도 있다. 알림이 전송되고 소리가 재생될 때 시스템은 또한 기기에서 지원되는 진동을 일으킨다.

UIApplication 클래스의 메서드를 사용하여 스케줄링된 알림을 취소하거나 알림 목록을 얻어올 수 있다. 이러한 메서드에 대한 더 많은 정보는 UIApplication Class Reference를 참고하라. 로컬 알림을 구성하는 데에 관한 더 많은 정보는 Local and Remote Notification Programming Guide를 참고하라.

Understanding When Your App Gets Launched into the Background

백그라운드 실행을 지원하는 앱은 다가올 이벤트를 처리하기 위해 시스템에 의해 재실행되었을 수 있다. 만약 사용자가 강제로 끈 것이 아니라 다른 임의의 이유로 앱이 종료되었을 경우 시스템은 다음 중 하나의 이벤트가 일어날 때 앱을 실행한다.

  • 로컬 앱의 경우:
    • 시스템이 앱의 구성된 전송 기준에 맞는 위치 업데이트를 받은 경우.
    • 기기가 등록된 지역에 들어가거나 존재할 경우. (지역은 지리적 지역이나 iBeacon 지역이 해당될 수 있다.)
  • 오디오 앱의 경우, 오디오 프레임워크가 앱이 어떠한 데이터를 처리하기를 필요로 하는 경우. (오디오 앱은 오디오를 재생하거나 마이크를 사용하는 것을 포함한다.)
  • 블루투스 앱의 경우:
    • 중심 역할을 하는 앱이 연결된 주변 기기에서 데이터를 받는 경우.
    • 연결된 중심에서 명령을 받는 주변 기기의 역할을 하는 앱의 경우.
  • 백그라운드 다운로드 앱의 경우:
    • 푸시 알림이 도착하고 그 알림의 페이로드가 값이 1을 가진 content-available 키를 포함하는 경우.
    • 새로운 컨텐츠 다운로드를 시작하기 위해 앱을 깨우는 경우.
    • NSURLSession 클래스를 사용하여 백그라운드에서 컨텐츠를 다운로드하는 앱의 경우. 세션과 관련된 모든 작업은 성공적으로 완료하거나 에러를 받았을 때.
    • 뉴스스탠드 앱이 시작한 다운로드가 완료되었을 때.

대부분의 경우, 시스템은 사용자에 의해 강제로 종료된 앱을 재실행하지 않는다. 예외의 경우는 위치 앱으로 iOS 8 이상부터 사용자에 의해 강제 종료되더라도 재실행된다. 그 밖에 다른 경우에는 사용자가 명시적으로 앱을 실행하거나 기기를 재부팅해야 앱이 백그라운드에서 시스템에 의해 재실행된다. 기기에 비밀번호 보호가 활성화되어 있으면 시스템은 사용자가 기기를 처음 풀기 전까지 백그라운드에서 앱을 실행하지 않는다.

Being a Responsible Background App

포그라운드 앱은 항상 백그라운드 앱보다 시스템 자원과 하드웨어를 사용하는 측면에서 우위를 가진다. 백그라운드에서 실행되는 앱은 이러한 차별에 대해 준비할 필요가 있고 백그라운드에서 실행될 동작을 조정해야 한다. 명확하게 백그라운드로 이동하는 앱은 다음 가이드라인을 따라야 한다.

  • 코드에서 OpenGL ES 호출을 하지 마라.: 백그라운드에서 실행 중에 EAGLContext 객체를 생성하지 말아야 하고 어떠한 OpenGL ES 그리는 명령을 발행하지 말아야 한다. 이러한 호출을 사용하는 것은 앱을 즉시 종료시킨다. 또한 앱은 이전에 제출한 명령이 백그라운드로 이동 전에 완료가 되도록 해야 한다. 백그라운드로 이동하거나 백그라운드에서 오는 경우 OpenGL ES를 처리하는 데에 더 많은 정보는 OpenGL ES Programming GuideImplementing a Multitasking-aware OpenGL ES Application를 참고하라.
  • 일시 중지되기 전에 임의의 Bonjour 관련 서비스를 취소하라.: 앱이 백그라운드로 이동할 경우, 일시 중지되기 전에 앱은 Bonjour로부터 해제되어야 하고 네트워크 서비스와 관련된 수신 대기 소켓을 닫아야 한다. 일시 중지된 앱은 들어오는 서비스 요청에 대해 응답할 수 없다. 그러한 서비스를 폐쇄하는 것은 실제로 사용이 불가능할 때 사용이 가능한 것처럼 보이는 것을 방지한다. 만약 Bonjour 서비스를 폐쇄하지 않으면 시스템이 앱이 일시 중지될 때 닫는다.
  • 네트워크 기반 소켓의 연결 오류 처리에 대비하라.: 시스템은 여러 이유로 앱이 일시 중지된 동안 소켓 연결을 끊을 수 있다. 소켓 기반 코드가 다른 타입의 신호 손실이나 네트워크 전환 같은 네트워크 오류를 대비하는 한 이상한 문제는 발생하지 않는다. 앱이 다시 시작될 때 소켓 사용에 오류를 맞닿뜨릴 경우 단순히 연결을 재설정하면 된다.
  • 백그라운드로 이동하기 전에 앱의 상태를 저장하라.: 낮은 메모리 상태에서는 백그라운드 앱이 메모리에서 제거되어 공간을 확보할 수 있다. 일시 중지된 앱이 먼저 메모리에서 비워지고 비워질 때 앱에게 알림이 가지는 않는다. 그 결과, 앱은 iOS 6의 상태 보존 메커니즘의 이점을 활용하여 인터페이스 상태를 디스크에 저장해야 한다. 이러한 기능 지원에 대한 자세한 정보는 Preserving Your App's Visual Appearance Across Launches를 참고하라.
  • 백그라운드로 갈 때 필요없는 객체들의 강한 참조를 제거하라.: 만약 앱이 객체의 많은 메모리 캐시(특히 이미지 객체)를 유지하고 있을 경우, 백그라운드로 이동 시에 그 캐시에 있는 강한 참조를 제거하라. 더 많은 정보는 Reduce Your Memory Footprint를 참고하라.
  • 일시 중지되기 전에 공유 시스템 자원의 사용을 중지하라.: 주소록이나 달력 데이터베이스 같은 공유 시스템 자원과 상호 작용하는 앱은 일시 중지되기 전, 그러한 자원의 사용을 중지해야 한다. 그러한 자원에 대한 우선권은 언제나 포그라운드 앱에게 있다. 앱이 일시 중지될 때 공유 자원을 사용하고 있는 것이 발견되면, 앱은 종료된다.
  • 윈도우와 뷰의 갱신을 피하라.: 앱이 백그라운드에 있을 때 앱의 윈도우와 뷰는 보이지 않기 때문에, 그것들을 업데이트하는 것을 피해야 한다. 예외는 앱이 스냅샷을 만들기 전에 윈도우의 컨텐츠를 갱신할 필요가 있을 때이다.
  • 외부 액세서리의 연결과 연결 해제 알림에 응답하라.: 외부 액세서리랑 상호 작용하는 앱은 앱이 백그라운드로 이동했을 때 시스템이 자동으로 연결 해제 알림을 보낸다. 앱은 이 알림에 등록해야 하고 현재 액세서리 세션을 폐쇄할 때 이것을 사용해야 한다. 앱이 포그라운드로 다시 돌아올 때, 일치하는 연결 알림이 보내지며 앱에게 재연결할 수 있는 기회를 준다. 액세서리 연결과 연결 해제 알림에 대한 더 자세한 정보는 External Accessory Programming Topics를 참고하라.
  • 백그라운드로 이동할 때 활성화 경고에 대한 자원을 정리하라.: 앱을 전환할 때 컨텍스트를 저장하기 위해 시스템은 앱이 백그라운드로 이동할 때 자동으로 action sheets(UIActionSheet)나 alert views(UIAlertView)를 닫지 않는다. 백그라운드로 이동하기 전에 적절한 정리 작업을 제공하는 것은 당신에게 달려 있다. 예를 들어 action sheet나 alert view를 프로그래밍으로 취소시키거나 나중에 뷰를 복원하는 데 충분한 상황 정보를 저장하기를 원할 수 있다 (앱이 종료되었을 경우).
  • 백그라운드로 이동하기 전에 뷰의 민감한 정보를 제거하라.: 앱이 백그라운드로 전환되면 시스템은 앱의 메인 윈도우의 스냅샷을 찍어 포그라운드로 돌아올 때 잠깐 보여준다. applicationDidEnterBackground(_:) 메서드가 반환되기 전에 스냅샷의 부분으로 캡쳐될 수 있는 패스워드나 민감한 개인 정보를 가리거나 흐리게 해야 한다.
  • 백그라운드 실행 시에 최소한의 작업을 하라.: 백그라운드 앱에게 주어진 실행 시간은 포그라운드 앱에게 주어지는 시간의 양보다 제한되어 있다. 백그라운드 실행 시간이 너무 긴 앱은 시스템에 의해 다시 조절되거나 종료될 수 있다.

백그라운드 오디오 앱이나 백그라운드에서 실행이 허용되는 다른 유형의 앱을 구현하는 경우 그 앱은 일반적인 방법으로 들어오는 메시지에 응답한다. 즉, 시스템은 메모리 부족 경고가 일어날 때 앱에게 알려줄 것이다. 그리고 시스템이 앱을 종료함으로써 메모리를 확보해야하는 상황의 경우 그 앱은 종료하기 전 최종 작업을 수행하기 위해 applicationWillTerminate:를 호출한다.

Opting Out of Background Execution

만약 앱이 백그라운드에서 돌아가는 것을 원치 않는다면 앱의 Info.plist 파일에서 UIApplicationExitsOnSuspend 키(값을 YES)를 추가함으로써 백그라운드를 선택 해제할 수 있다. 앱이 선택 해제하면, 비실행, 비활성화, 활성화 상태를 순환하고 백그라운드나 일시 중지 상태에는 들어가지 않는다. 유저가 홈 버튼을 눌러 앱을 종료하면, 앱 델리게이트의 applicationWillTeraminate 메서드가 호출되고 앱은 종료되기 전 정리 및 종료되는 5초의 시간이 주어진 뒤 실행하지 않는 상태로 이동한다.

백그라운드 실행을 선택 해제하는 것은 매우 권장하지 않지만 특정 조건 하에서 선호되는 옵션일 수도 있다. 특히 백그라운드 실행을 위한 코딩이 앱에 중대한 복잡성을 더하는 경우, 앱을 종료하는 것이 더 쉬운 해결책이 될 수 있다. 또한 앱이 많은 양의 메모리를 소비하고 쉽게 놓지 않는 경우, 시스템은 다른 앱을 위한 공간을 만들기 위해 빨리 앱을 종료할지도 모른다. 그래서 백그라운드로 전환하는 대신 종료하는 옵션은 동일한 결과를 발생시키고 개발 시간과 노력을 줄일 수도 있다.

Info.plist 파일에 포함된 키에 대한 더 많은 정보는 Information Property List Key Reference를 참조하라.

Reference