iOS 8 앱 확장에 대한 튜토리얼
게시 됨: 2022-03-11이전에 시도한 사람은 거의 없었지만(이를 살펴보세요), 스마트폰과 모바일 OS의 모습을 정의한 것은 최초의 iPhone을 출시한 Apple이었습니다. Apple은 하드웨어와 사용자 경험에서 놀라운 혁신을 이루었습니다. 그러나 모바일 OS가 작동하는 방식과 스마트폰 애플리케이션을 만드는 방식에 대한 표준도 설정한다는 사실을 종종 잊습니다.
애플리케이션 사이에 콘크리트 벽을 구축하여 애플리케이션을 완전히 격리시키고 서로를 인식하지 못하게 하는 것이 애플리케이션을 안전하게 유지하고 데이터를 보호하는 최선의 방법이었습니다. 모든 활동은 iOS에서 면밀히 모니터링되었으며 앱이 범위 밖에서 수행할 수 있는 작업은 소수에 불과했습니다.
"금욕은 최고의 보호입니다!" - 하지만 거기에 재미가 어디 있습니까?
시간이 좀 걸렸습니다. 당신이 나에게 묻는다면 너무 길지만 iOS 8과 함께 Apple은 약간의 재미를 갖기로 결정했습니다. iOS 8은 App Extensions라는 새로운 개념을 도입했습니다. 이 새로운 기능은 애플리케이션 사이의 벽을 허물지 않았지만 일부 앱 사이에 부드러우면서도 확실한 접촉을 제공하는 몇 개의 문을 열었습니다. 최신 업데이트는 iOS 개발자에게 iOS 생태계를 사용자 정의할 수 있는 옵션을 제공했으며 이 경로도 열리기를 간절히 바랍니다.
iOS 8 앱 확장이란 무엇이며 어떻게 작동합니까?
간단히 말해서 iOS 8 App Extensions는 앱을 시작하거나 화면에 표시하지 않고 애플리케이션과 상호 작용하는 새로운 방법을 제공합니다.
예상대로 Apple은 모든 것의 최상위에 머물도록 했기 때문에 애플리케이션이 제공할 수 있는 몇 가지 새로운 진입점이 있습니다.
- 오늘(위젯이라고도 함) - 알림 센터의 오늘 보기에 표시되는 확장 프로그램은 간단한 정보를 표시하고 빠른 작업을 수행할 수 있도록 합니다.
- 공유 - 앱이 소셜 네트워크 및 기타 공유 서비스에서 사용자와 콘텐츠를 공유할 수 있도록 하는 확장입니다.
- 작업 - 사용자가 호스트 앱에서 시작된 콘텐츠를 보거나 변환할 수 있도록 작업 시트에 사용자 지정 작업 버튼을 만들 수 있는 확장입니다.
- 사진 편집 - 사용자가 사진 앱 내에서 사진이나 비디오를 편집할 수 있게 해주는 확장 프로그램입니다.
- 문서 제공자 - 다른 앱이 귀하의 앱에서 관리하는 문서에 액세스할 수 있도록 하는 데 사용되는 확장입니다.
- 사용자 지정 키보드 - 시스템 키보드를 대체하는 확장입니다.
앱 확장은 독립 실행형 앱이 아닙니다. 그들은 효율적이고 단일 작업에 집중하기 위한 앱의 확장된 기능(호스트 앱이라고 하는 다른 앱에서 액세스할 수 있음)을 제공합니다. 고유한 바이너리, 고유한 코드 서명 및 고유한 요소 집합이 있지만 포함하는 앱의 바이너리의 일부로 App Store를 통해 제공됩니다. 하나의(포함하는) 앱에는 둘 이상의 확장이 있을 수 있습니다. 사용자가 확장 기능이 있는 앱을 설치하면 iOS에서 사용할 수 있습니다.
예를 살펴보겠습니다. 사용자가 Safari를 사용하여 사진을 찾고 공유 버튼을 누르고 공유할 응용 프로그램 확장을 선택합니다. Safari는 확장 기능을 로드하고 표시하는 iOS 소셜 프레임워크와 "대화"합니다. 확장 프로그램의 코드가 실행되고 시스템의 인스턴스화된 통신 채널을 사용하여 데이터를 전달하고 작업이 완료되면 Safari가 확장 프로그램 보기를 분해합니다. 이 직후 시스템은 프로세스를 종료하고 애플리케이션은 화면에 표시되지 않습니다. 그러면서도 사진 공유 기능을 완성했다.
프로세스 간 통신을 사용하는 iOS는 호스트 앱과 앱 확장이 함께 작동할 수 있도록 하는 책임이 있습니다. 개발자는 확장 지점과 시스템에서 제공하는 고급 API를 사용하므로 기본 통신 메커니즘에 대해 걱정할 필요가 없습니다.
라이프 사이클
App Extensions는 iOS 앱과 수명 주기가 다릅니다. 호스트 앱은 사용자 작업에 대한 응답으로 확장의 수명 주기를 시작합니다. 그런 다음 시스템은 앱 확장을 인스턴스화하고 이들 간의 통신 채널을 설정합니다. 확장 프로그램의 보기는 호스트 앱의 요청에서 수신된 항목을 사용하여 호스트 앱의 컨텍스트 내에서 표시됩니다. 확장 프로그램의 보기가 표시되면 사용자는 확장 프로그램과 상호 작용할 수 있습니다. 사용자의 작업에 대한 응답으로 확장 프로그램은 작업을 즉시 수행/취소하거나 필요한 경우 백그라운드 프로세스를 시작하여 수행하여 호스트 앱의 요청을 완료합니다. 그 직후에 호스트 앱은 확장 프로그램의 보기를 분해하고 사용자는 호스트 앱 내에서 이전 컨텍스트로 돌아갑니다. 이 프로세스를 수행한 결과는 프로세스가 완료되면 호스트 앱으로 반환될 수 있습니다. 확장은 일반적으로 호스트 앱에서 받은 요청을 완료한 후(또는 이를 수행하기 위해 백그라운드 프로세스를 시작한 후) 곧 종료됩니다.
시스템은 호스트 앱에서 사용자 작업의 확장을 열고, 확장은 UI를 표시하고, 일부 작업을 수행하고, 호스트 앱에 데이터를 반환합니다(확장 유형에 적합한 경우). 포함하는 앱은 확장 프로그램이 실행되는 동안에도 실행되지 않습니다.
앱 확장 만들기 - Today 확장을 사용한 실습 예제
위젯 이라고도 하는 Today 확장은 알림 센터의 Today 보기에 있습니다. 이는 사용자에게 최신 콘텐츠를 제공하거나(예: 기상 조건 표시) 빠른 작업(예: 할 일 목록 앱의 위젯에서 완료한 작업 표시)을 수행하는 좋은 방법입니다. 여기서 키보드 입력이 지원되지 않는다는 점을 지적해야 합니다.
앱의 최신 정보를 표시하는 Today 확장 프로그램을 만들어 보겠습니다(GitHub의 코드). 이 코드를 실행하려면 프로젝트에 대한 앱 그룹을 (재)구성했는지 확인하십시오(개발 팀을 선택하고 앱 그룹 이름은 고유해야 하며 Xcode의 지침을 따라야 함을 명심하십시오).
새 위젯 만들기
이전에 말했듯이 앱 확장은 독립 실행형 앱이 아닙니다. 앱 확장을 빌드할 포함하는 앱이 필요합니다. 포함하는 앱이 있으면 File -> New -> Target into Xcode로 이동하여 새 대상을 추가하도록 선택합니다. 여기에서 Today Extension을 추가할 새 대상에 대한 템플릿을 선택합니다.
다음 단계에서는 제품 이름을 선택할 수 있습니다. 알림 센터의 오늘 보기에 표시될 이름입니다. 이 단계에서도 Swift와 Objective-C 중에서 언어를 선택하는 옵션이 있습니다. 이 단계를 마치면 Xcode는 Info.plist
파일과 인터페이스 파일(스토리보드 또는 .xib 파일)이 있는 주 클래스( TodayViewController
라고 함)에 대한 기본 헤더 및 구현 파일을 제공하는 Today 템플릿을 만듭니다. Info.plist
파일은 기본적으로 다음과 같습니다.
<key>NSExtension</key> <dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.widget-extension</string> </dict>
템플릿에서 제공하는 스토리보드를 사용하지 않으려면 NSExtensionMainStoryboard
키를 제거하고 뷰 컨트롤러의 이름과 함께 NSExtensionPrincipalClass
키를 값으로 추가합니다.
오늘 위젯은 다음을 수행해야 합니다.
- 콘텐츠가 항상 최신 상태인지 확인
- 사용자 상호 작용에 적절하게 응답
- 잘 수행(iOS 위젯은 메모리를 현명하게 사용해야 하며 그렇지 않으면 시스템에서 종료됩니다)
데이터 공유 및 공유 컨테이너
앱 확장 프로그램과 포함하는 앱은 모두 비공개로 정의된 공유 컨테이너의 공유 데이터에 액세스할 수 있습니다. 이는 포함하는 앱과 확장 프로그램 간의 간접적인 통신 방법입니다.
Apple이 이러한 것들을 "단순"하게 만드는 방법이 마음에 들지 않습니까? :)
NSUserDefaults
를 통해 데이터를 공유하는 것은 간단하고 일반적인 사용 사례입니다. 기본적으로 확장 프로그램과 포함하는 앱은 별도의 NSUserDefaults
데이터 세트를 사용하며 서로의 컨테이너에 액세스할 수 없습니다. 이 동작을 변경하기 위해 iOS는 앱 그룹 을 도입했습니다. 포함하는 앱 및 확장에서 앱 그룹을 활성화한 후 [NSUserDefaults standardUserDefaults]
를 사용하는 대신 [[NSUserDefaults alloc] initWithSuiteName:@"group.yourAppGroupName"]
을 사용하여 동일한 공유 컨테이너에 액세스합니다.
위젯 업데이트
콘텐츠가 항상 최신 상태인지 확인하기 위해 Today 확장은 위젯의 상태를 관리하고 콘텐츠 업데이트를 처리하기 위한 API를 제공합니다. 시스템은 때때로 위젯 보기의 스냅샷을 캡처하므로 위젯이 표시되면 보기의 라이브 버전으로 교체될 때까지 가장 최근의 스냅샷이 표시됩니다. NCWidgetProviding
프로토콜을 준수하는 것은 스냅샷이 찍히기 전에 위젯의 상태를 업데이트하는 데 중요합니다. 위젯이 widgetPerformUpdateWithCompletionHandler:
호출을 수신하면 위젯의 보기가 가장 최신 콘텐츠로 업데이트되어야 하며 업데이트 결과를 설명하기 위해 다음 상수 중 하나로 완료 핸들러를 호출해야 합니다.

-
NCUpdateResultNewData
- 새 콘텐츠를 보려면 보기를 다시 그려야 합니다. -
NCUpdateResultNoDate
- 위젯을 업데이트할 필요가 없습니다. -
NCUpdateResultFailed
- 업데이트 프로세스 중 오류가 발생했습니다.
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResultFailed // If there's no update required, use NCUpdateResultNoData // If there's an update, use NCUpdateResultNewData [self updateTableView]; completionHandler(NCUpdateResultNewData); }
위젯을 볼 수 있을 때 제어하기
위젯이 표시되는 시기를 제어하려면 NCWidgetController
클래스의 setHasContent:forWidgetWithBundleIdentifier:
메소드를 사용하십시오. 이 방법을 사용하면 위젯 콘텐츠의 상태를 지정할 수 있습니다. 위젯 또는 포함하는 앱(활성화된 경우)에서 호출할 수 있습니다. 이 메소드에 NO
또는 YES
플래그를 전달하여 위젯 컨텐츠가 준비되었는지 여부를 정의할 수 있습니다. 콘텐츠가 준비되지 않은 경우 오늘 보기가 열릴 때 iOS에서 위젯을 표시하지 않습니다.
NCWidgetController *widgetController = [[NCWidgetController alloc] init]; [widgetController setHasContent:YES forWidgetWithBundleIdentifier:@"com.your-company.your-app.your-widget"];
위젯에서 포함하는 앱 열기
Today 위젯은 openURL:completionHandler:
메서드를 호출하여 포함하는 앱 열기를 요청할 수 있는 유일한 확장입니다. 포함하는 앱이 사용자의 현재 작업 컨텍스트에서 의미 있는 방식으로 열리도록 하려면 사용자 지정 URL 체계(위젯과 포함하는 앱 모두 사용할 수 있음)를 정의해야 합니다.
[self.extensionContext openURL:[NSURL URLWithString:@"customURLsheme://URLpath"] completionHandler:nil];
UI 고려 사항
위젯을 디자인할 때 UIVisualEffectView
클래스를 활용하세요. 흐리게/생동감 있게 표시해야 하는 보기는 UIVisualEffectView
가 아니라 contentView
에 직접 추가해야 합니다. NCWidgetProviding
프로토콜을 준수하는 위젯은 마지막 viewWillDisappear:
의 뷰 상태와 일치시키기 위해 viewWillAppear:
에서 캐시된 상태를 로드해야 하며, 새로운 데이터가 도착하면 새 데이터로 원활하게 전환해야 합니다. 이는 일반 보기의 경우가 아닙니다. 컨트롤러(UI는 viewDidLoad
에서 설정되고 애니메이션을 처리하고 viewWillAppear
에서 데이터 로드). 위젯은 작업을 수행하거나 탭 한 번으로 포함하는 앱을 열 수 있도록 설계되어야 합니다. 위젯 내에서 키보드 입력을 사용할 수 없습니다. 즉, 텍스트 입력이 필요한 UI를 사용해서는 안 됩니다.
위젯에 수직 및 수평 스크롤을 추가하는 것은 불가능합니다. 또는 더 정확하게는 스크롤 보기를 추가할 수 있지만 스크롤이 작동하지 않습니다. Today 확장의 스크롤 보기에서 가로 스크롤 제스처는 알림 센터에서 가로채어 Today에서 알림 센터로 스크롤됩니다. Today 확장 프로그램 내에서 스크롤 보기를 세로로 스크롤하는 것은 Today's View를 스크롤하여 중단됩니다.
기술 노트
여기에서는 App Extension을 만들 때 염두에 두어야 할 몇 가지 중요한 사항을 지적하겠습니다.
모든 확장에 공통적인 기능
다음 항목은 모든 확장에 적용됩니다.
sharedApplication 개체가 제한 없음: 앱 확장은 sharedApplication 개체에 액세스하거나 해당 개체와 관련된 메서드를 사용할 수 없습니다.
카메라 및 마이크가 꺼짐 : 앱 확장은 장치의 카메라 또는 마이크에 액세스할 수 없습니다(그러나 이것은 모든 하드웨어 요소의 경우가 아님). 이는 일부 API를 사용할 수 없는 결과입니다. 앱 확장의 일부 하드웨어 요소에 액세스하려면 해당 API를 앱 확장에 사용할 수 있는지 여부를 확인해야 합니다(위에서 설명한 API 가용성 확인 사용).
대부분의 백그라운드 작업은 제한 이 없습니다. 앱 확장은 아래에서 설명하는 업로드 또는 다운로드 시작을 제외하고 장기 실행 백그라운드 작업을 수행할 수 없습니다.
AirDrop이 사용 중지됨 : 앱 확장은 AirDrop을 사용하여 데이터를 받을 수 있지만 보낼 수는 없습니다.
백그라운드에서 업로드/다운로드
백그라운드에서 수행할 수 있는 작업 중 하나는 NSURLSession object
를 사용하여 업로드/다운로드하는 것입니다.
업로드/다운로드 작업이 시작된 후 확장은 호스트 앱의 요청을 완료하고 작업 결과에 영향을 주지 않고 종료될 수 있습니다. 백그라운드 작업이 완료될 때 확장 프로그램이 실행되고 있지 않으면 시스템은 백그라운드에서 포함하는 앱을 시작하고 앱의 대리자 메서드 application:handleEventsForBackgroundURLSession:completionHandler:
가 호출됩니다.
확장이 백그라운드 NSURLSession
작업을 시작하는 앱에는 포함하는 앱과 해당 확장이 모두 액세스할 수 있도록 설정된 공유 컨테이너가 있어야 합니다.
포함하는 앱과 각 앱 확장에 대해 서로 다른 백그라운드 세션을 생성해야 합니다(각 백그라운드 세션에는 고유 식별자가 있어야 함). 한 번에 하나의 프로세스만 백그라운드 세션을 사용할 수 있기 때문에 이것은 중요합니다.
행동 vs. 공유
Action 및 Share 확장 간의 차이점은 실제로 매우 유사하기 때문에 코더의 관점에서 완전히 명확하지 않습니다. 공유 확장 대상에 대한 Xcode의 템플릿은 소셜 공유에 사용할 수 있는 표준 작성 보기 UI를 제공하는 SLComposeServiceViewController
를 사용하지만 필수는 아닙니다. 공유 확장은 완전한 사용자 정의 디자인을 위해 UIViewController에서 직접 상속할 수도 있습니다. 이는 Action 확장이 SLComposeServiceViewController
에서 상속할 수 있는 것과 같은 방식입니다.
이 두 가지 확장 유형의 차이점은 사용 방법에 있습니다. Action 확장을 사용하면 자체 UI가 없는 확장을 빌드할 수 있습니다(예: 선택한 텍스트를 번역하고 번역을 호스트 앱으로 반환하는 데 사용되는 확장). 공유 확장 프로그램을 사용하면 호스트 앱에서 바로 댓글, 사진, 비디오, 오디오, 링크 등을 공유할 수 있습니다. UIActivityViewController
는 Action 및 Share 확장을 모두 구동합니다. 여기서 Share 확장은 맨 위 행에 색상 아이콘으로 표시되고 작업 확장은 맨 아래 행에 흑백 아이콘으로 표시됩니다(이미지 2.1).
금지된 API
헤더 파일에 NS_EXTENSION_UNAVAILABLE
매크로 또는 사용할 수 없는 유사한 매크로로 표시된 API는 사용할 수 없습니다(예: iOS 8의 HealthKit 및 EventKit UI 프레임워크는 앱 확장에서 사용할 수 없음).
앱과 확장 프로그램 간에 코드를 공유하는 경우 앱 확장에 허용되지 않는 API를 참조하더라도 앱 스토어에서 앱이 거부된다는 점을 염두에 두어야 합니다. 공유 클래스를 계층 구조로 리팩토링하여 이 문제를 처리하도록 선택할 수 있습니다. 공통 상위 클래스와 다른 대상에 대한 다른 하위 클래스가 있습니다. 또 다른 방법은 #ifdef
검사를 통해 전처리기를 사용하는 것입니다. 아직 기본 제공 대상 조건이 없기 때문에 직접 만들어야 합니다.
이를 수행하는 또 다른 좋은 방법은 자체 임베디드 프레임워크를 만드는 것입니다. 확장에 사용할 수 없는 API가 포함되어 있지 않은지 확인하십시오. 포함된 프레임워크를 사용하기 위해 앱 확장을 구성하려면 대상의 빌드 설정으로 이동하고 "App-Extension-Safe API만 필요" 설정을 예로 설정합니다. Xcode 프로젝트를 구성할 때 파일 복사 빌드 단계에서 "Frameworks"를 임베디드 프레임워크의 대상으로 선택해야 합니다. "SharedFrameworks" 대상을 선택하면 App Store에서 제출을 거부합니다.
이전 버전과의 호환성에 대한 참고 사항
앱 확장은 iOS 8 이후로만 사용할 수 있지만 포함하는 앱을 이전 iOS 버전에서 사용할 수 있도록 할 수 있습니다.
Apple 휴먼 인터페이스 규정 준수
앱 확장을 디자인할 때 Apple의 iOS 휴먼 인터페이스 지침을 염두에 두십시오. 포함하는 앱이 지원하는 기기에 관계없이 앱 확장이 범용인지 확인해야 합니다. 앱 확장이 범용인지 확인하려면 Xcode에서 "iPhone/iPad" 값(범용이라고도 함)을 지정하는 대상 장치 제품군 빌드 설정을 사용합니다.
결론
앱 확장은 확실히 iOS 8에서 가장 가시적인 영향을 미칩니다. 79%의 기기가 이미 iOS 8을 사용하고 있기 때문에(2015년 4월 13일 App Store에서 측정), 앱 확장은 앱이 활용해야 하는 놀라운 기능입니다. API의 제한 사항과 확장 프로그램과 포함하는 앱 간에 데이터를 공유하는 방식을 결합하여 Apple은 보안 모델을 손상시키지 않으면서 플랫폼에 대한 가장 큰 불만 중 하나를 해결할 수 있었던 것 같습니다. 타사 앱이 서로 데이터를 직접 공유할 수 있는 방법은 아직 없습니다. 이것은 매우 새로운 개념이지만 매우 유망해 보입니다.