Today Extension 사용 예제
- by Tapito
iOS 8부터 새로 도입된 Today Extension을 구현하는 예제입니다.
1. 프로젝트 및 어플리케이션 확장 생성
Xcode를 열고 새 프로젝트를 생성합니다. 여기에서는 [iOS] - [Application] - [Single View Application]을 선택하고 Next를 누르겠습니다.
앱의 이름을 지정하고 Next를 누르면 빈 화면의 iOS 어플리케이션 프로젝트가 생성됩니다.
Today Extension은 본래의 앱과 따로 작동되는 또 하나의 작은 앱입니다. 이 프로젝트에서 Today Extension을 추가하기 위해 [File] - [New] - [Target...]을 클릭합니다.
[iOS] - [Application Extension] - [Today Extension]을 선택 후 Next를 클릭합니다.
적절한 이름을 지정한 후 Finnish를 클릭합니다. Bundle Identifier가 서로 다름을 통해 iOS 어플과 Today Extension이 따로 작동됨을 알 수 있습니다.
2. App Group 설정하기
사실상 두 앱이 작동되는 것이므로 둘 사이에 데이터를 교환할 필요가 있을 경우에는 App Groups를 사용합니다. 위 화면에서 먼저 주 어플리케이션인 ExtensionApp을 클릭하고 [Capablities]를 클릭한 후 [App Groups] 옆의 스위치를 클릭하여 [On] 상태로 만듭니다.
** 주의사항 1: App Groups를 사용하기 위해서는 1년에 $99를 지불하고 유효기간이 남아있는 애플 개발자 ID가 필요합니다. 애플 서버를 통하는 인증이므로 기기 탈옥으로 이 제약을 우회할 수 없습니다.
주 어플리케이션과 Today Extension 사이에 데이터를 교환할 필요가 없다면 이 과정이 필요하지 않습니다. $99를 지불하기에는 아까운데 꼭 데이터를 교환할 필요가 있다면 공인 IP로 접근 가능한 외부 서버를 하나 마련하여 데이터를 전송하고 대상 앱 또는 Today Extension이 그 데이터를 다시 전송받는 형식으로 약간의 꼼수를 사용할 수는 있습니다.
개발자 ID로 로그인한 후 provision을 선택하고 [Choose] 버튼을 클릭합니다.
이미 만들어 놓은 App Groups에서 하나를 선택할 수도 있고 하단의 [+] 버튼을 클릭하여 새 App Groups를 만들수도 있습니다.
** 주의사항 1: App Groups는 전 세계에서 유일한 이름이어야 합니다. group.compant.Test 류의 흔한 이름은 이미 누군가가 애플 개발자 서버에 등록했을 가능성이 높고 이 경우 오류가 뜨며 App Group 생성이 되지 않습니다. 본인이나 회사의 이름을 포함하여 최대한 중복을 피해야 합니다.
** 주의사항 2: 한번 등록한 App Groups는 지우기가 불가능합니다. 이름 지정에 신중해야 합니다.
주 어플리케이션의 확장인 TodayExtensionApp도 마찬가지로 App Groups를 활성화합니다. 주 어플리케이션과의 데이터 공유를 위해 이전 단계에서 선택한 App Group과 같은 App Group를 선택해야 합니다.
3. 어플리케이션과 Today Extension에 대한 UI 작성 및 Outlet 연결
문자열 교환을 확인하기 위해 UI를 대충 작성합니다. 이와 관련한 자세한 과정은 생략합니다. Today Extension과 관련된 UI는 Xcode 좌측 탐색 메뉴에서 MainInterface.storyboard에 있습니다.
4. 데이터 교환을 위한 코드 작성
주 어플리케이션과 Today Extension 사이에 데이터를 교환하기 위해서는 NSUserDefaults 클래스를 사용합니다. 이 때 식별자는 App Groups에서 선택하거나 새로 만든 ID로서 주 어플리케이션과 Today Extension이 함께 공유하며 모두 접근 가능합니다.
4.1 주 어플리케이션 측 코드 ([확인] 버튼을 터치했을 때의 이벤트 처리)
/* ViewController.m */ - (IBAction) buttonOK_TouchDown:(id)sender { /* suite name은 AppGroup에서 선택했던 식별자 중 하나를 지정합니다. */ NSUserDefaults * sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.company.Test"]; /* 공유되는 UserDefaults에 문자열 값을 저장합니다. forKey는 변수 이름으로, Today Extension도 이 이름이 붙은 변수를 불러올 것입니다. */ [sharedUserDefaults setObject:self.textField.text forKey:@"MyString"]; /* 변경 사항을 저장하고 이 UserDefaults를 공유하는 앱들에게 데이터가 변경되었음을 통지합니다. */ [sharedUserDefaults synchronize]; }
4.2 Today Extension 측 코드 (같은 UserDefaults를 공유하는 다른 앱에서 Synchronize 메서드가 실행될때마다 자신의 값을 업데이트)
/* TodayViewController.m */ - (id) initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { /* 시스템 수준에서 어떤 NSUserDefaults가 synchronize 되었음을 감지하면 이를 자신에게도 알려달라고 자신을 등록합니다. */ /* 동일한 App Groups에 속한 다른 앱에서 NSUserDefaults가 변경될 때마다 userDefaultsDidChange가 실행될 것입니다. */ [[NSNotificationCenter notificationCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil]; } return self; } - (void) viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view form its nib. [self refresh]; } - (void) userDefaultsDidChange:(NSNotification *)notification { [self refresh]; } - (void) refresh { NSUserDefaults * sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.company.Test"]; /* UserDefaults에 변동 사항 발생 시 또는 사용자가 상태표시줄을 끌어 내려 이 UI가 보여질때마다 MyString 변수의 값을 다시 읽어옵니다. */ self.labelContent.text = [sharedUserDefaults objectForKey:@"MyString"]; }
5. 컴파일 및 실행하기
Today Extension을 생성하고 주 어플리케이션과의 데이터 교환을 위한 기본적인 작업은 끝났습니다. 컴파일 후 실행해보겠습니다.
주 어플리케이션이 뜨는데, 상태 표시줄을 아래로 끌어 내리면 아래와 같이 Today Extension을 볼 수 있습니다.
New Widget Available이라 알림이 뜨는데 Edit를 터치하여 현재 만든 Today Extension을 화면에 보이도록 추가합니다.
5.1 레이아웃 관련 오류 해결
여기서 만든 Today Extension의 제목은 뜨는데 레이아웃에서 넣은 각종 Label 은 보이지 않습니다. 혹은 아래가 잘려서 보일 수도 있는데요, Interface Builder에서 작성한 Today Extension의 높이를 코드로 정확히 명시하면 이 문제가 해결됩니다. iOS Simulator로 테스트 하는 경우 화면 상단의 [iOS Simulater] - [Reset Content and Settings...]를 클릭하여 시뮬레이터를 초기화하고 실행하는 것을 권장합니다.
/* TodayViewController.m */ - (void) viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view form its nib. [self setPreferredContentSize:CGSizeMake(320.0, 200.0)]; [self refresh]; }
작성한 UI가 그대로 Today Extension에 나타남을 확인할 수 있습니다. 그런데 iOS의 Today Extension에서 각 뷰들을 일종의 들여쓰기처럼 보여주고 있어 본래 작성한 UI보다 한 쪽으로 치우쳐져 있음을 확인할 수 있습니다. 이 현상을 수정하기 위해 Today Extension에 대한 Margin을 없애도록 아래와 같이 메서드를 하나 override합니다.
/* TodayViewController.m */ - (UIEdgeInsets) widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets { /* 이 레이아웃에 대한 Margin을 0으로 합니다. */ return UIEdgeInsetsZero; }
다시 실행하면 레이아웃이 제대로 보여짐을 확인할 수 있습니다.
Today Extension을 닫고 주 어플리케이션으로 돌아가 label에 들어갈 텍스트를 수정해보겠습니다. [확인] 버튼을 눌러 Today Extension에게 UserDefaults가 수정되었음을 통지하면 아래와 같이 Today Extension이 UserDefault 내용을 다시 읽어 변경된 문자열을 출력해 줌을 확인할 수 있습니다.
참고 사이트: iOS 8 Today Extension Tutorial
'Application Programming Interface > Cocoa' 카테고리의 다른 글
UITableView에 CustomTableViewCell 적용하기 (0) | 2015.04.20 |
---|---|
코딩으로 Nib 첫 화면 불러오기 (0) | 2015.03.30 |
키보드 보이기/숨김에 따른 UITextView 및 UITextField의 가려짐 현상 해결법 (0) | 2015.03.27 |
Xcode Interface Builder를 사용하여 사용자 정의 컨트롤 만들기 (2) | 2015.01.26 |
[코코아 터치] 숫자를 다루는 3 컨트롤 (0) | 2012.03.11 |