[최종 수정일 : 2018.09.17]

원문 : Background Modes Tutorial: Getting Started

백그라운드 모드 자습서(Background Modes Tutorial: Gettring Started)

이 튜토리얼에서, 가장 일반적인 백그라운드 모드를 사용하는 앱을 만들 것입니다: 오디오 재생, 위치 업데이트, 일반 작업, 백그라운드에서 가져오기

2010년에 iOS 4에서, 애플은 멀티태스킹(multitasking을 도입하였습니다.

개발자와 사용자들은 서로 iOS 멀티태스킹 시스템이 허용하는 것이 무엇인지에 대해 혼란스러워 했습니다. 애플은 사용자 환경을 개선하고 배터리 수명을 연장하기 위해 백그라운드 작업에 제한을 주었습니다. 앱은 매우 특별한 경우에 백그라운드에서 계속 실행하도록 허용됩니다. 예를들어, 오디오 재생, 가져온 위치 업데이트하거나 서버에서 최신 컨텐츠를 가져오는 것입니다.

하려는 작업(task)이 이러한 카테고리에 속하지 않은 경우에, 백그라운드 모드를 사용할 수 없습니다. 원하는 목적의 범위를 벗어나서 백그라운드 모드를 사용해서 시스템을 속이는 경우에 앱 스토어에서 리젝(rejection) 될수 있으니, 고민해봐야 합니다.

시작하기(Getting Started)

자료 다운로드(Download Materials)에서 프로젝트 파일을 다운로드 받을 수 있습니다. 사용자 인터페이스(user inteface)와 주제(topic)에 직접 적용할수 없는 몇 가지 로직을 보게 될것입니다.

프로젝트를 살펴보기(digging) 전에, iOS에서 사용할수 있는 기본 백그라운드 모드에 대한 간략한 개요를 봅니다.. Xcode에서, 앱 대상(targe)의 기능(Capabilities)탭을 누르면 다음과 같은 목록을 볼수 있습니다.

백그라운드 모드가 있는 기능 목록 가능 방법:

  1. Project navigator에서 프로젝트 선택
  2. 앱 대상 클릭
  3. Capabilities탭 선택
  4. Background Modes 스위치를 On으로 설정

이 백그라운드 모드 자습서에서, 백그라운드 처리를 하는 4가지 방법에 대해서 알아볼것입니다.

  • 오디오 재생(Play audio): 앱은 백그라운드에서 오디오를 재생/녹음을 계속 할 수 있습니다.
  • 위치 업데이트 수신(Receive location updates): 앱은 디바이스의 위치가 변경됨에 따라 콜백을 계속 받을수 있습니다.
  • 한정된 시간만큼 작업 수행(Perform finite-length tasks): 앱이 제한된 시간동안 임의의 코드를 실행할 수 있는, 일반적인 무엇이든(whatever)의 경우
  • 백그라운드 가져오기(Background Fetch): iOS에 의해 스케쥴(예약)되어 최근 컨텐츠에 대한 업데이트

이러한 모드 중에 하나 또는 몇가지에 관심이 있는 경우, 자유롭게 건너띌 수 있습니다.

프로젝트를 실행하기 전에, 다음에 보는것처럼, 개발자 팀을 설정해야 합니다.

샘플 프로젝트를 실행시켜보세요. 4개의 탭(각각 하나의 모드를 담당)으로 되어 있습니다.

주의
완벽한 효과를 보려면, 실제 기기에서 실행해야 합니다. 구성 설정을 잊은 경우에, 앱은 시뮬레이터의 백그라운드에서 제대로 실행되는 것처럼 보일수 있습니다. 하지만, 실제 기기에서는 전혀 동작하지 않을 수 있습니다.

오디오 재생(Playing Audio)

제일 먼저, 백그라운드 오디오

iOS에서 오디오를 재상하는 몇가지 방법이 있고 대부분 재생할 오디오 데이터를 더 많이 제공하기 위해 콜백(callback) 구현이 필요합니다.

스트리밍 데이터로 오디오를 재생하는 경우에, 네트워크 연결을 시작할수 있고, 연결 콜백(callback)은 오디오 데이터를 계속 제공합니다.

오디오 백그라운드 모드를 활성화 할때, iOS는 앱이 현재 활성(active) 앱이 아닌경우에도 이 콜백을 계속 합니다. 맞습니다 - 오디오 백그라운드 모드는 사실상 자동입니다. 단지 백그라운드 모드를 활성화하고 적절하게 처리할 수 있는 기반(infrastructure)를 제공하면 됩니다.

AudioViewController.swift을 열어 주세요.

앱은 노래를 큐(queue)에 넣고 차래대로 재생하기 위해 AVQueuePlayer를 사용합니다. 그 뷰 컨트롤러는 업데이트를 제공하기 위해 플레이어의 currentItem을 감시(observing)합니다.

감사 드립니다(Giving Credit Where Credit Is Due)

시작 프로젝트는, 로얄티가 없는 음악 사이트incompetech.com의 오디오 파일이 포함되어 있습니다. 감사하게도, 음악을 무료로 사용할 수 있습니다. 여기에 있습니다.

갑사합니다. 캐빈(Kevin)!

앱이 활상 상태 일때, 음악 제목 라벨이 보이고, 백그라운드 일때, 콘솔(console)에서 제목을 출력합니다. 라벨 텍스트는 백그라운드에 있는 동안에 업데이트 할 수 있지만, 여기에서 중요한 것은 앱이 백그라운드에 있을때 앱이 계속 콜백(callback)을 받을 수 있다는 것을 증명하는 것입니다.

주의
활성 상태와 그외에 대해서 자세히 배우고 싶으면 앱의 실행 상태(Execution States for Apps)를 확인해 보세요.

빌드하고 실행하면 다음과 같이 보여야 합니다.

이제 Play를 탭하면 음악이 시작됩니다.

백그라운드에서 오디오 테스트하기(Testing Audio in the Background)

실제 기기에서 실행하는 동안에, 홈 버튼을 누르면 음악이 멉출것입니다. 왜 그럴까요? 가장 중요한 부분이 빠져 있습니다.

대부분의 백그라운드 모드는(무엇이든(whatever)모드는 제외) , 앱이 백그라운드에 있는 동안 코드가 실행하길 원한다는 것을 알려주는 기능이 가능하도록 해줘야 합니다.

Xcode로 돌아가서 다음과 같이 합니다.

  1. Project navigator에서 프로젝트를 클릭
  2. TheBackgrounder 대상(target)을 선택
  3. Background Modes로 스크롤을 내리고 스위치를 on으로 함
  4. Audio, AirPlay and Picture in Picture를 클릭

다시 빌드하고 실행합니다. 음악을 시작하고 홈 버튼을 누릅니다. 이번에는 앱이 백그라운드에 있더라도, 음악을 계속 들을 수 있습니다.

Xcode의 Console 출력에서 시간이 업데이트 되는 것을 보게 되며, 앱이 백그라운드에 있어도 코드가 동작하는 것을 증명합니다.

와우~, 이미 오디오 플레이어가 있으면, 백그라운드에서 오디오를 재생하는 것은 쉽습니다(easy)! 음, 이것은 한가지 모드입니다. 백그라운드 모드 자습서 전부 따라하면 3가지 모드를 배우게 됩니다.

위치 업데이트 수신하기(Receiving Location Updates)

위치 백그라운드 모드(Location background mode)일때, 앱은 백그라운드에 있을때에도, 여전히 사용자의 업데이트된 위치와 함께 위치 위임(delegate) 메시지를 수신합니다. 이러한 위치 업데이트의 정확도(accuracy)을 제어 할 수 있고 앱이 백그라운드에서 실행하는 동안에도 정확도를 변경할 수 있습니다.

두번째 탭은 위치 업데이트에 대한 것이며, LocationViewController.swift를 열어 주세요.

이 컨트롤러에서, CCLocationManager가 실행하는 것을 보게 될것 입니다. 위치 정보를 수신하기 위해 , CCLocationManager인스턴스를 생성하고 구성합니다. 이 경우에, 사용자가 화면에서 UISwitch를 활성화 했을때, 앱은 위치를 모니터링 합니다. 위치 업데이트를 수신함에 따라, 앱은 지도에서의 핀(pins)을 그립니다. 앱이 백그라운드에 있을때, Xcode 콘솔 출력창에서 위치 업데이트 로그를 보게 됩니다.

위치 업데이트 활성화 하기(Enabling Location Updates)

주목해야 하는 곳은 CCLocationManager인스턴스에 있는 requestAlwaysAuthorization()입니다. 이는 iOS 8 이후로 요구되었고, 백그라운드에서 위치 수신할 수 잇는 권한을 묻는 대화상자가 나타납니다.

이제 백그라운드 모드(background modes)에 익숙해졌으니, 이전과 같은 실수 할 필요는 없습니다. 앱이 백그라운드에 있는 동안에 위치 업데이트를 수신을 계속 하길 원하는 것을 iOS가 알수 있도록 Location updates에 대한 박스를 체크합니다.

이 박스를 체크하는 것 외에도, iOS 8 이후부터는 사용자에게 왜 백그라운드 모드가 필요한지 설명하는 키를 info.plist에 설정해야 합니다. 이것을 포함하지 않으면, 위치 요청은 조용히(silently) 실패합니다. 시작 프로젝트는 이미 이러한 키가 추가되어 있습니다.

  • Xcode에서 프로젝트를 선택
  • TheBackgrounder대상(target)에 대한 Info탭 클릭
  • Privacy - Location Always and When In Use Usage Description과 Privacy - Location When In Use Usage Description 키를 찾습니다.
  • 왜 백그라운드에서 위치 업데이트 수신이 필요한지에 대해 설득력있게 설명을 작성

이제, 빌드하고 실행합니다! 두번재 탭으로 전환하고 왼쪽 상단 모서리에 있는 스위치를 ON으로 전환합니다.

처음에 이것을 하려고 할때, 여러분이 위치 개인정보 이유(location privacy reason)로 작성한 메시지를 보게 될 것입니다. Always Allow를 누르고 건물 밖이나 주변을 산책합니다 (엉뚱하게 포켓몬을 잡으려 하지 말고). 시뮬레이터에서도 위치 업데이트를 볼수 있습니다.

잠시후에 다음과 같이 보일 것입니다.

백그라운드 모드에서 위치 테스트하기(Testing Location Mode in the Background)

앱이 백그라운드에 있는 경우에, 콘솔 로그에서 앱이 위치를 업데이트 하는 것을 봐야 합니다. 앱을 다시 열고 앱이 백그라운드에 있는 동안에 업데이트된 위치에 대한 모든 핀을 지도에서 볼 수 있을 것입니다..

시뮬레이터를 사용하는 경우에, 시뮬레이터 이동을 사용할수 있습니다. Debug > Location 메뉴를 확인해 보세요.

아주 쉽습니다. 그렇죠? 세번째 탭에서 세번재 백그라운드 모드입니다.

한정된 시간만큼 작업 수행하기 또는 무엇이든(Performing Finite-Length Tasks… or, Whatever)

다음 백그라운드 모드는 공식적(officially)으로 백그라운드에서 한정된 시간만큼 작업 실행하기(Executing a Finite-Length Task in the Background)라고 합니다.

기술적으로, 이것은 백그라운드 모드가 아니며, Capabilities에서 앱이 이 모드를 사용한다고 선언할 필요가 없습니다. 그 대신에, 앱이 백그라운드에 있을때, 한정된 시간만큼 임의의 코드를 실행할 수 있는 API입니다. 음… 뭐든간에!

한정된 시간만큼 작업을 수행할때(When to Perform Finite-Length Tasks)

Whatever 백그라운드 모드와 매우 유효한 사용 사례는 카메라 롤에 비디오를 랜더링하고 쓰는 것처럼, 약간 긴 시간동안 작업을 완료하는 것입니다.

하지만 이것은 한가지 예제일 뿐입니다. 실행할수 있는 코드는 임의적이며(arbitrary), 이 API를 꽤 많은 일을 하는데 사용할 수 있습니다: 오랫동안 계산을 수행하기(이 자습서에서 수행할 것), 이미지에 필터 적용하기, 복잡한 3D 매쉬 그리기.., 무엇이든지! 무제한이 아니라 약간의 시간동안에만 유지할수 있는 것처럼, 여러분이 상상하는 것은 제한이 있습니다.

iOS는 앱이 백그라운드로 이동한 후에 얼마나 많은 시간만큼 기다릴지 결정합니다. 부여받은(granted) 시간에 대한 보장은 없지만, 항상 UIApplication.shared에 있는 backgroundTimeRemaining으로 확인 할수 있습니다. 이것은 얼마나 많은 시간이 남았는지 알려줍니다.

일반적으로, 옵져버 기반(observation-based)은 3분을 얻게 됩니다. 다시 말하지만, 그것은 보장하지 않고(no guarantees) API 문서는 대략적인 수치(ballpark number)조차 없습니다.-숫자에 연연해 하지마세요. 5분 또는 5초가 될수 있으며, 앱이 이에 대해서 준비할 필요가 있습니다.

다음은.모든 CS 학생이 숙지해야 하는 공통 작업입니다: 피보나치 수열(Fibonacci Sequence)에서 숫자 계산하기. 여기에서 트위스트(twist)는 백그라운드에서(in the background) 이 숫자를 계산 한다는 것입니다.

한정된 시간만큼 작업 설정하기(Setting Up a Finite-Length Task)

WhateverViewController.swift을 열고 이미 있는 것을 찾아봅니다. 현재 그대로, 이 뷰는 피보나치 수열에서 수를 계산하고 결과를 보여줄 것입니다. 실제 기기에서 앱을 일시 중단(suspend)하는 경우에, 그 계산은 중단되고 앱이 다시 활성화 될때 어디에서 시작하는지를 찾습니다. 여러분이 해야 할 일은 iOS가 더 이상 없다(no more)!라고 말할때까지 계산을 계속 하도록 백그라운드 작업을 생성하는 것입니다.

먼저 WhateverViewController에 다음과 같은 프로퍼티를 추가하는 것이 필요합니다.

var backgroundTask: UIBackgroundTaskIdentifier = .invalid

이 프로퍼티는 백그라운드에서 실행하는 작업 요청을 식별합니다.

다음으로 WhateverViewController에 다음 메소드를 추가합니다

func registerBackgroundTask() {
  backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
    self?.endBackgroundTask()
  }
  assert(backgroundTask != .invalid)
}
  
func endBackgroundTask() {
  print("Background task ended.")
  UIApplication.shared.endBackgroundTask(backgroundTask)
  backgroundTask = .invalid
}

registerBackgroundTask()는 앱이 백그라운드로 이동하는 경우에 수행하는 작업이 무엇이든간에, iOS에게 완료하는데 시간이 더 필요하다는 것을 말합니다. 이것을 호출하고 나서, 앱이 백그라운드로 이동하는 경우, endBackgroundTask()를 호출할때까지 CPU를 사용할 수 있을 것입니다.

거의 다 왔습니다. 백그라운드에서 일정 시간이 지난 후에 endBackgroundTask()를 호출하지 않는 경우, iOS는 beginBackgroundTask(expirationHandler:)를 호출할때 정의된 클로져를 호출해서 코드 실행을 멈출수 있도록 합니다. endBackgroundTask()를 호출해서 OS에게 완료된 것을 알려주는 것이 가장 좋은 생각입니다. 이것을 하지 않고 이 블록을 실행한 후에도 코드를 계속 실행하면, iOS는 앱을 종료시킬 것입니다.

백그라운드 작업 등록하고 끝내기(Registering & Ending Background Tasks)

이제, 주용한 부분입니다. 백그라운뜨 작업을 등록하고 끝내기 위해 didTapPlayPause(_:)를 업데이트해야 합니다. 이 메소드에서 추가 해야할 주석(comments) 2개가 있습니다.

register background task 주석 아래에 registerBackgroundTask()를 호출합니다.

registerBackgroundTask()

계산을 시작할때 registerBackgroundTaks()호출해서 백그라운드에서도 숫자 계산을 계속 할수 있도록 합니다.

이제, end background task 주석 아래에 다음 블록을 추가합니다.

if backgroundTask != .invalid {
  endBackgroundTask()
}

이제, 사용자가 계산을 멈추고자 할때, iOS에게 더 이상 CPU를 사용할 필요가 없다는 것을 알려주기 위해, endBackgroundTask()를 호출합니다.

beginBackgroundTask(expirationHandler:)을 호출할때마다 endBackgroundTask()를 호출하는 것이 중요합니다. beginBackgroundTeask(expirationHandler:)을 두번 호출하고 하나의 작업에 대해서만 endBackgroundTask()를 호출하는 경우, 두번째 백그라운드 작업 식별자로 두번째 endBackgroundTask()를 호출할때까지 CPU를 계속 사용합니다. 이것이 backgroundTask가 필요한 이유입니다.

사용자 인터페이스 업데이트하기(Updating the User Interface)

앱의 상태에 따라 다르게 동작하도록 calculateNextNumber()메소드를 업데이트 합니다.

메소드의 마지막 줄에 있는 resultLabel.text = resultsMessage를 다음에 오는 코드로 교체합니다.

switch UIApplication.shared.applicationState {
  case .active:
    resultsLabel.text = resultsMessage
  case .background:
    print("App is backgrounded. Next number = \(resultsMessage)")
    print("Background time remaining = " +
    "\(UIApplication.shared.backgroundTimeRemaining) seconds")
  case .inactive:
    break
}

앱이 활성화 될때에만 라벨을 업데이트 합니다. 앱이 백그라운드로 이동할때, 콘솔에 새로운 결과와 백그라운드 시간이 얼마나 남았는지 알려주는 메시지를 출력합니다.

빌드하고 실행하고나서 세번째 탭으로 전환합니다.

Play를 누르고 앱이 계산하는 값을 보게 됩니다. 홈 버튼을 누르고 Xcode의 콘솔에서 출력을 봅니다. 남은 시간이 줄어드는 동안에 앱이 숫자를 업데이트 하는 것을 보게됩니다.

대부분의 경우에, 이 시간은 180(180초 = 3분)에서 시작해서 5초까지 내려갑니다. 5초에 도달 해서(특정 조건에 따라 다른 값이 될수 있음) 시간이 만료할대까지 기다리는 경우에, iOS는 만료 블록을 호출하고 앱은 출력하는 것을 멈추게 됩니다. 그리고나서, 앱으로 돌아가면, 타이머가 다시 시작하고 이 미친짓(madness)을 계속될 것입니다.

백그라운드에서 시간 만료 처리하기(Handling Timer Expiration With Backgrounding)

이 코드에는 버그가 하나 있습니다. 앱이 백그라운드에 있고 할당된 시간이 만료될때까지 기다리고 있다고 가정해봅니다. 이 경우에, 앱은 만료 핸들러를 호출하고 endBackgroundTask()를 호출하므로, 백그라운드 시간이 필요하지 않게 됩니다.

그런 다음에 앱으로 돌아가면, 타이머가 계속 실행될 것입니다. 하지만 앱을 다시 나가면, 백그라운드 시간을 전혀 얻지 못할 것입니다. 왜 그럴까요? 만료와 백그라운드로 돌아가는 중에 beginBackgroundTask(expirationHandler:) 호출하는 앱이 없기 때문 입니다.

어떻게 해결할 수 있을까요? 여기에는 여러가지 방법이 있고 그중에 하나는 상태 변경 알림을 사용하는 것입니다.

앱의 실행 상태(Execution States for Apps)에 대한 애플의 문서에서 상태 변경에 대한 응답을 받는 방법에 대한 모든 상세 정보를 볼수 있습니다.

버그를 고칠 시간입니다. 우선, 새 메소드 reinstateBackgroundTask()를 추가합니다.

@objc func reinstateBackgroundTask() {
  if updateTimer != nil && backgroundTask ==  .invalid {
    registerBackgroundTask()
  }
}

타이머가 실행중이고 백그라운드 작업이 유효하지 않은 경우에만 복원해야 합니다. 한가지 일을 하는 작은 유틸리티 함수로 코드를 쪼개는 것이 좋습니다. 이 경우에 단순히 registerBackgroundTask()를 호출하면 됩니다.

이제 viewDidLoad()를 오버라이드(override)하고 다음과 같이 추가해서 UIapplicationDidBecomeActiveNotification을 구독(subscribe)합니다.

override func viewDidLoad() {
  super.viewDidLoad()
  NotificationCenter.default
    .addObserver(self, selector: #selector(reinstateBackgroundTask), 
                 name: UIApplication.didBecomeActiveNotification, object: nil)
}

앱이 활성화 될때마다. iOS는 새 메소드 reinstateBackgroundTask()를 호출합니다.

알림을 구독할때마다, 구독 취소에 대해서도 생각해야 합니다. deinit을 사용합니다. 다음을 추가하세요.

deinit {
  NotificationCenter.default.removeObserver(self)
}

주의
iOS 에서는 더이상 옵져버를 삭제하지 않아도 되지만, 이렇게 하는 것은 좋은 습관입니다.

여러분은 해제를 가지고 있습니다 : 이제 iOS가 괜찮다고 말하는 한, 무엇이든(whatever) 원하는데로 할 수 있습니다.

백그라운드 모드 자습서의 마지막 주제는 백그라운드 가져오기(Background Fetching) 입니다.

백그라운드 가져오기(Background Fetch)

백그라운드 가져오기는 iOS7에서 도입되었으며, 배터리 수명에 영향을 최소화하면서 앱을 항상 최신 정보로 표시할 수 있도록 해줍니다. 예를 들어, 앱에서 뉴스피드를 구현하고 있다고 가정(suppose)해봅시다. 백그라운드 가져오기 전에, viewWillAppear(_:)에서 새 데이터를 가져오는 작업을 할 수 있습니다.

이 해결책의 문제는 새로운 데이터가 들어올때까지 사용자가 적어도 몇초동안 이전 데이터를 보고 있는 것입니다. 앱을 열었을때 새로운 데이터가 마술처럼 있다면 더 좋지 않을까요? 이것이 백그라운드 가져오기가 제공하는 것입니다.

활상화 될때, 시스템은 사용자 패턴을 사용하여 백그라운드 가져오는 가장 효과적인 시기를 결정합니다. 예를 들어, 사용자가 매일 오전 9시에 앱을 여는 경우에, 그 시간 전에(before) 백그라운드 가져오기가 발생할 가능성이 있습니다. 시스템은 백그라운드 가져오기하는 최적의 시간을 결정하고 중요한 업데이트를 수행할때 사용해서는 안됩니다.

백그라운드 가져오기 설정하기(Setting Up a Background Fetch)

백그라운드 가져오기를 구현하기 위해, 반드시 해야할 3가지가 있습니다.

  • 앱의 Capabilities의 Background Mod에 있는 Background fetch 박스를 체크
  • 앱에 적절한 시간 간격을 설정하기 위해 setMinimumBackgroundFetchInterval(_:)을 사용
  • 앱 델리게이터에 있는 application(_:performFetchWithCompletionHandler:)를 구현해서 백그라운드 가져오기 처리

이름에서 알수 있듯이, 백그라운드 가져오기는 일반적으로 네트워크 서비스 같은 외부 소스에서 정보를 가져오는 작업을 포함합니다(involves). 이 백그라운드 모드 자습서의 목적을 위해, 현재 시간을 가져올 것이고 네트워크를 사죵하지는 않습니다.

한정된 시간만큼 작업하기와는 달리, 백그라운드 가져오기를 할때 단지 몇 초 동안만 동작합니다. - 합의된 수치는 최대 30초이지만, 더 짧을수록 좋습니다. 가져오기에서 큰 리소스를 다운로드해야 할 경우에, URLSession의 백그라운드 전송 서비스를 사용해야 합니다.

세부 구현(Implementation Details)

시작할 시간입니다. FetchViewController.swift를 열고 무엇을 하는지 보세요.

fetch(_:) 메소드는 외부 소스로부터 데이터를 가져오기 위해 실제 할수 있는 것들을 단순하게 대체한 것입니다(JSON이나 XML RESTful 서비스). 데이터를 가져와서 분석하는데 몇초가 걸리수 있으므로, 처리가 완료되면 호출하는 완료 핸들러를 전달합니다. 이것이 왜 중요한지 나중에 알게 될것입니다.

updateUI() 시간 형식을 정하고 보여줍니다. updateLabel의 주변에 있는guard문은 뷰가 실제로 로딩되었는지 확인합니다. time이 옵셔널 타입이므로, 설정되지 않은 경우에, 아직 업데이트 되지 않음(Not Updated yet) 메시지를 보여줍니다.

뷰를 처음 로딩할때, 가져오지 않지만 updateUI()로 이동하며ㅡ 아직 업데이트 되지 않음(Not Updated yet) 메시지가 먼저 나타납니다. 마지막으로, 사용자가 Update를 누를때, 가져오기가 수행되고, 완료 핸들러에서 UI를 업데이트 합니다.

뷰 컨트롤러가 동작하며, 여러분이 할일은 없습니다.

하지만 백그라운드 가져오기를 사용할 수 없습니다.

백그라운드 가져오기를 활성화 하는 1 단계(Step One to Enabling Background Fetch)

백그라운드 가져오기를 활성화하는 첫번째 단계는 앱의 Capabilities탭에 있는 백그라운드 가져오기(Background fetch)를 확인합니다.

백그라운드 가져오기를 활성화 하는 2 단계(Step Two to Enabling Background Fetch)

다음으로, AppDelegate.swift를 열고 application(_:didFinishLaunchingWithOptions:)에 다음을 추가합니다.

UIApplication.shared.setMinimumBackgroundFetchInterval(
  UIApplication.backgroundFetchIntervalMinimum)

이것은 백그라운드 가져오기 간격을 최소한으로 설정해서, 백그라운드 가져오기를 요청합니다. 기본 간격은 UIApplicationBackgroundFetchIntervalNever이며, 다시 전환할 수 있습으며. 예를들어, 사용자가 로그아웃하고 더 이상 업데이트가 필요하지 않은 경우입니다. 초단위로 특정 간격을 설정 할수 있습니다. 시스템은 백그라운드 가져오기(fetch)가 발생하기 전에 최소한 몇초정도 기다립니다.

서버에 연결하는한 불필요하게 배터리를 사용하기 때문에, 값을 너무 작게 설정하지 않도록 주의해야 합니다. 결국에는, 가져오기(fetch)의 정확한 타이밍은 시스템에 달려있지만, 적어도 수행하기 전에 정해진 간격만큼은 기다릴 것입니다. 일반적으로 UIApplicationBackgroundFetchIntervalMinimum은 사용하기에 가장 좋은 값입니다.

백그라운드 가져오기를 활성화 하는 3 단계(Step Three to Enabling Background Fetch)

마지막으로, 백그라운드 가져오기를 활성화하기 위해서, 반드시 application(_:performFetchWithCompletionHandler:)을 구현해야 합니다. AppDelegate에 추가합니다.

// Support for background fetch
func application(
  _ application: UIApplication, 
  performFetchWithCompletionHandler 
    completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  //1
  if let tabBarController = window?.rootViewController as? UITabBarController,
    let viewControllers = tabBarController.viewControllers {

    //2
    for viewController in viewControllers {
      if let fetchViewController = viewController as? FetchViewController {
        //3
        fetchViewController.fetch {
          //4
          fetchViewController.updateUI()
          completionHandler(.newData)
        }
      }
    }
  }
}

위의 내용은 다음과 같습니다.

  1. 모든 앱에서 rootViewController이 UITabBarController가 필요한것이 아니기 때문에 rootViewController을 UITabBarController으로 변환하는 것은 선택적입니다. 하지만, 이 앱에서는 rootViewController이 항상 UITabBarController이며, 이 코드가 절대 실패하지 않는 것을 의미합니다.
  2. 탭바 컨트롤러에서 뷰 컨트롤러를 반복해서 찾고 FetchViewController로 변환이 성공하는 하나를 찾습니다. 이 앱에서, 마지막 뷰 컨트롤러인것을 알고 있기에 하드코딩(hard-code) 할 수 있지만, 반복문(looping)을 사용하는것이 나중에 탭에 추가하거나 삭제할 경우에 더 견고하게 해줍니다.
  3. fetch(_:) 호출합니다.
  4. fetch(_:)가 완료될때, UI를 업데이트하고 매개변수로 전달된 completionHandler을 호출합니다. 작업이 끝날때 완료 핸들러를 호출하는 것은 중요합니다. 첫번째 가져오기 하는 동안에 발생된 일을 첫번째 매개변수로 지정합니다. 가능한 값은 .newData, .noData, .failed입니다.

시간을 가져오는 것은 결코 실패하지 않고 항상 마지막 호출과는 다르기에, 이 튜토리얼에서는 .newData를 지정합니다. iOS는 이 값을 사용해서 백그라운드 가져오기(fetch) 시간을 개선 할 수 있습니다. 이 시점에 시스템은 앱의 스탭샷을 찍어 앱 전환시 카드 뷰에 표시할 수 있는 것을 알고 있있고, 그게 전부입니다.

주의
완료 클로져를 따라서 전달하기보다는, 변수 프로퍼티에 저장하고 가져오기가 완료될때 호출 할 수 있습니다. 이렇게 하지 마세요. application(_:performFetchWithCompletionHandler:)을 여러번 호출하는 경우에, 이전 핸들러는 덮어씌여지고 호출되지 않습니다. 이런 종류의 프로그래밍 오류는 불가능하기에, 핸들러를 전달하고 이를 호출하는게 좋습니다.

백그라운드 가져오기 테스트(Testing Background Fetch)

백그라운드 가져오기 테스트하는 한가지 방법은 시스템이 이를 처리하기로 결정하는 것을 기다립니다. 그것은 오랫동안 기다려야 합니다. 다행히도, Xcode는 백그라운드 가져오기를 시뮬레이터하는 방법을 제공합니다. 테스트 해야할 두가지 시나리오가 있습니다. 하나는 앱이 백그라운드에 있을대이고 다른 하나는 일시 정지(suspended)된 상태에서 돌아올때 입니다. 첫번째 방법은 가장 쉽고 메뉴만 선택하면 됩니다.

  • 실제 기기에서 앱을 시작(시뮬레이터가 아님)
  • Fetch탭으로 이동
  • Not yet updated 메시지를 받음
  • Xcode에 있는 Debug로부터, Simulate Background Fetch 선택

  • Xcode에서 앱을 백그라운드로 보냅니다. 앱이 디버깅을 멈추면, 디버깅을 계속하세요.

  • 앱을 다시 엽니다.
  • 시간이 업데이트 됩니다.

앱을 정시상태에서 다시 시작할때 테스트하기(Testing When the App Is Resumed From Suspension)

앱을 직접 시작시킬수 수 있는 시작 옵션이 있습니다. 자주 테스트 할수도 있기에, 해당 옵션으로 새로운 Scheme를 만드는 것이 가장 좋습니다. Xcode는 이를 쉽게 합니다.

첫번째, Manage Schemes옵션을 선택

다음으로, 목록에서 유일한 스키마(scheme)를 선택하고, 기어 아이콘을 클릭하고 Duplicate를 선택합니다.

마지막으로, Background Fetch처럼, 스키마를 적당한 이름으로 교체하고, Launch due to a background fetch event 체크박스를 체크합니다.

스키마로 앱을 실행합니다. 앱이 실제로 열리지 않지만 일시정지된 상태로 시작될 것입니다.이제 수동으로 시작하고 Fetch 탭으로 이동합니다. 앱을 실행했을때, Not yet updated 대신에 업데이트된 시간이 있는지 봅니다.

백그라운드 가져오기 모드를 사용해서 실제로 사용자 최신 콘텐츠를 완벽하게 가져올수 있습니다.

여기에서 어디로 가나요?(Where to Go From Here?)

완료된 프로젝트 파일을 Download Materials에서 다운로드 할수 있습니다.

백그라운드 모드에 대해 여기에서 다룬 내용의 애플 문서를 읽고 싶으면, 백그라운드 실행(Background Execution)이 시작하기 가장 좋습니다. 이 문서는 모든 백그라운드 모드를 설명하고 각각의 문서의 해당 섹션에 대한 링크가 있습니다.

이 문서에서 특히 흥미로운 부분은 믿을수 있는 백그라운드 앱 만들기(being a responsible background app)에 관해 이야기 하는 것입니다. 여기에는 백그라운드에서 실행되는 앱을 출시하기 전에 앱과 관련있거나 관련 없는 몇가지 세부사항이 있습니다.

마지막으로, 백그라운드에서 대규모 네트워크 전송을 계획한 경우에, URLSession을 확인해야합니다.

Posted by 까칠코더