◎위챗 : speedseoul
https://tapito.tistory.com/450
http://www.glimsoft.com/06/28/ios-8-today-extension-tutorial/
iOS 8부터 새로 도입된 Today Extension을 구현하는 예제입니다.
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이 따로 작동됨을 알 수 있습니다.
사실상 두 앱이 작동되는 것이므로 둘 사이에 데이터를 교환할 필요가 있을 경우에는 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를 선택해야 합니다.
문자열 교환을 확인하기 위해 UI를 대충 작성합니다. 이와 관련한 자세한 과정은 생략합니다. Today Extension과 관련된 UI는 Xcode 좌측 탐색 메뉴에서 MainInterface.storyboard에 있습니다.
주 어플리케이션과 Today Extension 사이에 데이터를 교환하기 위해서는 NSUserDefaults 클래스를 사용합니다. 이 때 식별자는 App Groups에서 선택하거나 새로 만든 ID로서 주 어플리케이션과 Today Extension이 함께 공유하며 모두 접근 가능합니다.
/* 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]; }
/* 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"]; }
Today Extension을 생성하고 주 어플리케이션과의 데이터 교환을 위한 기본적인 작업은 끝났습니다. 컴파일 후 실행해보겠습니다.
주 어플리케이션이 뜨는데, 상태 표시줄을 아래로 끌어 내리면 아래와 같이 Today Extension을 볼 수 있습니다.
New Widget Available이라 알림이 뜨는데 Edit를 터치하여 현재 만든 Today Extension을 화면에 보이도록 추가합니다.
여기서 만든 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 내용을 다시 읽어 변경된 문자열을 출력해 줌을 확인할 수 있습니다.