KVC (Key-Value-Coding) 


    KVC는 문자열을 이용해 property에 접근할 수 있게 해주는 기능이다. 접근자를 이용하지 않고 property의 이름인 문자열로 접근하므로, 객체간 의존성을 낮추고 결합도가 낮은 소프트웨어 개발을 할 수 있게 해준다. KVC를 구현한 메소드는 NSKeyValueCoding 프로토콜에 의해 정의되며, NSObject는 이 프로토콜을 implement한 상태이다. KVC는 아래와 같은 로직으로 동작한다.


1. key와 일치하는 property를 찾는다.

2. 일치하는 property가 없을 경우, key와 일치하는 인스턴스 변수를 찾는다. 

3. 일치하는 property나 인스턴스 변수가 있으면, 이를 적용한다. 없으면 'valueForUndefinedKey:'나 'setValue:forUndefinedKey:'를 호출한다.


    위와 같은 로직으로 동작하기 때문에 property를 통한 직접적 접근보다 느리며, runtime에서 접근하기 때문에 key가 일치하지 않아 crash가 발생하기도 한다. 소프트웨어 구조적 측면에서 유연성이 필요한 경우에만 사용하는 것이 좋다.


    KVC는 아래에서 언급할 KVO와 Cocoa binding, Core Data, Apple script의 기술적 기반이 된다.


    collection에 KVC를 적용할 경우, 사용할 수 있는 특수한 property들이 생긴다. 해당 property들은 아래와 같다.


@count

@avg

@max

@min

@sum

@distinctUnionOfObjects

@unionOfObjects

@distinctUnionOfArrays

@unionOfArrays

@distinctUnionOfSets




KVO (Key-Value-Observing)


    KVO는 객체의 property가 변경되는 것을 알고 싶을 때 사용하는 기능이다. 위에서 설명한 KVC와 같이 keypath를 통한 property의 변경을 감지하고 알려준다. 

@property


    클래스 내의 멤버 변수에 대하여 접근자를 생성한다. @property를 사용할 경우, 추가로 attribute를 부여할 수 있다. attribute들은 아래와 같다. '*'로 표시된 attribute는 선언하지 않아도 기본적으로 적용되는 attribute들이다.


getter= : 접근자 중 getter의 메소드 이름을 정의한다.


setter= : 접근자 중 setter의 메소드 이름을 정의한다.


readwrite *  : getter/setter를 모두 만든다. Mutually exclusive로 readwrite를 한다.


readonly : 오직 getter만 생성한다. 해당 property에 값을 할당하려고 하면 컴파일 오류가 발생한다.


assign * : setter를 통해 간단한 할당만 할 뿐 reference count를 증가 시키지 않는다.


retain : 외부에서 해제된 객체의 참조 문제를 막기 위해 reference count를 증가시킨다.


copy : 할당하는데 있어, 객체의 복사본을 사용한다. copy를 이용하려면 NSCopying 프로토콜을 구현한 객체여야 한다.


nonatomic : 멀티 프로세서 환경에서 Mutual exclusive lock으로 접근자를 보호하지 말라고 지시하는 것이다. 기본적으로 nonatomic을 선언하지 않으면 atomic으로 동작한다.



@synthesize


    @property를 사용할 경우, getter/setter를 @impementation에 구현해야 한다. @synthesize는 사용하여 작성해주는데, modern objective-C에서는 @synthesize를 작성할 필요 없이 접근자를 자동으로 생성해준다.



@dynamic


    @synthesize를 대신하여 사용할 수 있는 지시어로, getter/setter를 자신의 클래스가 아닌 다른 곳에 구현되어 있음을 알려주는 지시어이다. 이 지시어를 사용할 경우, getter/setter가 구현되어 있지 않을 경우라도 컴파일러 경고를 받지 않는다.

    Complication은 복잡한 요소를 말하는 것으로 시계에서 시간을 가르쳐주는 페이스를 제외한 부속 화면 등을 얘기한다. watchOS2.0으로 업데이트되면서 Apple watch는 Complication을 지원하게 됬다. 사용자에세 간략한 정보를 신속하게 실시간(실제로는 사용자가 보는 순간)으로 업데이트하고 싶을 때 사용하면 좋다. 단, 여기서 중요한 것은 '간략한'이다. 데이터 처리 혹은 네트워크를 활용한 데이터 접근 등의 overload 많은 데이터 처리는 삼가는 것이 좋다. 이를 위해 watch app 내부에 data를 캐싱하는 것도 좋은 방법이다. 이제 Complication에 대해 하나씩 알아보자.




    Complication은 모두 5가지의 family를 제공한다. 5가지의 모양을 가지고 있는 것을 말한다. 각 5가지의 모양은 정보를 표시하기 위한 영역이 상이 하므로 이를 고려하여 Complication을 구현해야한다. 




Complication 동작 원리


    Clock kit에서 Complication에 표시하기 위한 데이터가 필요할 때, CLKComplicationDataSource를 구현한 객체를 통해 데이터에 접근한다. 객체의 메소드를 통해 데이터를 Clock kit에 제공하며 현재의 데이터 뿐만 아니라 구현 형태에 따라서 과거, 미래의 데이터도 보여줄 수 있다.

    Complication에 표시되는 데이터는 타임라인 형태로 구축하여 사용한다. Clock kit은 각 시간에 맞춰 타임라인으로 구축된 데이터를 읽어와 Complication에 바로 표시해준다. 또한 타임라인 형태로 데이터를 구축하면 Time Travel 기능을 지원하기 쉽다.

    Time Travel은 디지털 크라운을 이용하여, Complication에서 재공하는 과거와 현재의 데이터를 볼 수 있는 기능이다. Time line에 맞춰서 데이터를 구축할 때는 Date 값에 맞는 적절한 데이터를 산출하는 Logic이 중요하다. 예를 들어 일반적인 앱은 Date 당시의 데이터를 표시하지만 일정 앱은 다음 일정에 대한 안내를 보여줘야 한다. 각 Date 값에 따른 데이터 Logic 구현에 각별히 신경쓰도록 한다. 




Complication을 제공하기 위한 기준


    구현하기에 앞서, Complication을 통한 정보 표시 필요성에 관하여 고려해봐야 한다. Complication은 제한적인 공간에 시간 제약적인 데이터를 표시하기 때문에 구현했을 때, 효용이 떨어질수도 있기 때문이다. 아래는 Apple에서 제시하는 Complication 구현 기준이다. 이를 참조하면 Complication 구현 여부에 대한 판단에 도움이 될 것이다.


1. Complication은 공간이 매우 제약적이다. 몇몇 글자나 작은 이미지 정도만 표시 가능하다. 이 작은 공간을 통해 유용한 데이터를 전달할 수 있는지 확인해야한다.


2. Notification을 통해서 시간에 민감한 정보들을 전달하고 있다면 Complication을 고려해보는 것도 좋은 방법이다. 예를 들어 스포트 앱의 실시간 스코어를 Notification으로 알리는 것보다 Complication으로 알리는 것이 더 나을 수도 있다.


3. 데이터가 자주 업데이트된다면 데이터를 표시하기 위해 많은 시간을 할애하게 된다. 이는 Complication의 간결함에 많은 영향을 준다. 데이터의 업데이트가 자주 일어난다면 Complication 구현을 지양하는 것이 좋다.




Complication DataSource 구현


    DataSource 객체는 CLKComplicationDataSource 프로토콜을 구현한 클래스의 객체이다. 앞서 말한 바와 같이 구현된 클래스는 runtime에 객체를 생성하지 않는다. Xcode 프로젝트 설정에서 해당 클래스를 지정하면 Clock kit에 의해 필요할 때 객체를 생성하여 사용하게 된다. 생성된 객체는 다른 클래스들처럼 'init'메소드에 의해 초기화가 이루어진다. 만약에 네트워크를 이용해서 데이터를 읽거나 복잡한 연산을 통해서 데이터를 추출해야하는 경우, Clock kit에서 데이터를 불러오는데 많은 지연 시간을 유발한다. 이를 피하기 위해 iOS app 혹은 Watch extension을 통해 미리 계산하거나 로딩한 데이터를 캐시한다. DataSource 객체는 앞서 캐시한 데이터를 읽어오도록 구현하는 것이 바람직한다.




Timeline Data 구현 (TimeTravel 지원)


    먼저 Complication에 표시하기 위해 준비된 데이터를 객체를 불러온다. 해당 객체를 CLKComplicationTimelineEntry 객체로 만들어 Clock kit에 넘겨줘야 한다. CLKComplicationTimelineEntry 객체에 text나 image를 표시해야한다면 기존 앱처럼 raw 포맷으로 표시할 수 없다. Complication을 표시하기 위해서는 CLKTextProvider와 CLKImageProvider 객체를 사용한다. 


*TimeTravel에 관한 구체적 구현 사례 필요




Complication Data 업데이트


    Runtime에서 Clock kit을 총해 Complication Data를 업데이트하는 방법 몇 가지를 소개한다.


1. WatchKit extension이 실행 중일 때, 데이터를 업데이트한다.

2. Clock kit을 통해 주기적으로 WatchKit extension을 깨워 데이터를 업데이트한다.

3. 데이터 업데이트를 위해 Notification을 사용한다.

4. iOS app에서 시간에 민감한 데이터를 전달한다.


    Complication에 업데이트를 해야할 새로운 데이터가 있다면 CLKComplicationServer 객체를 이용하면 된다. 해당 객체의 'reloadTimelineForComplciation:'이나 'extendTimlineForComplication:' 메소드를 사용해야 한다. 새로운 데이터가 존재하지 않을 때는 굳이 해당 메소드를 호출하면 안된다. 해당 메소드의 호출 회수는 OS에 의해 제한되어 있다. 할당된 호출 회수를 다 사용하고 나면 회수가 복구될 때까지 Complication을 업데이트할 수 없기 때문에 새로운 데이터가 존재할 때만 신중하게 호출하도록 구현한다.


    각 업데이트 싸이클이 완료될 때마다 다음 업데이트 시점을 정하기 위해 'getNextRequestUpdateDateWithhandler' 메소드가 호출된다. 이 때 지정된 다음 업데이트 싸이클이 되면 DataSource 객체에서 'requestUpdateDidBegin'과 'requestUpdateBudgeExhausted' 메소드를 호출하게 된다. 이 메소드들 안에서 Complication 데이터 업데이트를 위해 호출했던 'reloadTimelineForComplciation:'이나 'extendTimlineForComplication:' 메소드를 사용하면 된다.


    iOS app에서 Complication 데이터를 업데이트해야 한다면 WKSession 객체의 'transferCurrentComplicationUserInfo:' 메소드를 사용한다. 해당 메소드를 호출하면 해당 메시지를 최우선순위로 하여, Clock kit이 강제로 Complication의 데이터 업데이트를 진행한다.




참고 : https://goo.gl/BWXGzQ

Fastlane은 iOS 프로젝트에 관한 전반적인 관리 툴이다. Fastlane은 다른 여러 유틸과 연계되어 다양한 서비스를 제공한다. 그 중, 필자가 사용해봤고 유용한 툴 몇가지를 소개한다.



*'sudo gem install [PACKAGE_NAME]'을 사용하여 설치할 때, El Capitan에서 권한 이슈에 부딪혀 설치가 안 되는 케이스가 발생한다. 이럴 때는 'vi ~/.gemrc'로 문서를 만들어 'gem: -n/usr-local/bin'을 추가해주도록 하자. 이는 시스템 bin 디렉토리의 접근 권한이 없어서 생기는 문제로 local 디렉토리로 설치 경로를 옮겨 해결할 수 있다.


Cert. 인증서 생성 및 갱신


Sigh. 프로비저닝 프로파일 생성 및 갱신


Pem. APNS 프로파일 생성 및 갱신


Gym. 빌드 자동화 및 아카이빙


Deliver. 앱스토어 등록 시, 스크린샷 업로드와 바이너리 등록 자동화


사용법 추후 정리 예정







FastLane : https://fastlane.tools

GitHub : https://github.com/fastlane/fastlane




    watchOS 2.0 (이하 watchOS 2)이 릴리즈된지 벌써 4개월이 지났다. 이제서야 watchOS 2를 정리하는 나의 천성적 게으름을 핀잔하며 이 글을 시작하려 한다. 참고로 이 포스팅은 Apple에서 제공하는 Transition Guide와 Document를 기반으로 작성됨을 알린다. watchOS 1.0 (이하 watchOS 1)에 대한 전반적 이해를 위해서는 이전 포스팅 (WatchKit : watchOS 1.0으로 개발하기)을 참고하기 바란다.



1. OS 업데이트로 인한 App 구조 변화


    watchOS 2에서 바뀐 가장 큰 소프트웨어적 차이점은 앱 소스 및 리소스를 관리하는 WatchKit Extension이 iOS app 부분에서 watch app으로 이동한 부분이다. 아래의 그림을 참고하면 알기 쉽다.





    위와 같은 변화로 인하여 기존에 Extension을 작성할 때는 iOS SDK를 활용하여 구현했지만 watchOS 2부터는 watchOS SDK를 활용해야 한다. 기존에 iOS SDK를 통한 기능 활용이 이 부분으로 인하여 제한이 걸린다. 그래서 watchOS 2에서 구현할 때는 iOS SDK가 아닌 watchOS SDK의 사용 가능 기술을 참고하여 구현해야 한다. 추가적으로 이전처럼 Extension에서 Shared Container를 이용한 iOS app과 watch app간의 데이터 전송이 불가능해졌다. 이를 해결하기 위해서는 iPhone과 Apple watch간의 무선 전송을 이용해야 한다.



2. watchOS 2에서 제공하는 추가 기능


    watchOS 2는 기존 watchOS 1에서 제공하던 일부 기능 외에 보다 많은 기술적 리소스를 활용할 수 있도록 기능이 많이 추가되었다. 추가된 기능의 목록은 아래와 같다. 그리고 무엇보다 NSURLSession을 통한 직접 다운로드가 가능하며 이는 background download를 제공한다.


Complication 기능 (ClockKit) : 자주 접근하는 정보를 Watch face를 통하여 표시

연락처 접근 (Contacts)

데이터 베이스 (Core Data)

Core Foundation (Core Foundation)

The Foundation (Foundation)

그래픽스 (Core Graphics)

위치 정보 (Core Location)

모션 인식 (Core Motion)

캘린더, 미리알림 (Event Kit)

건강 (Health Kit)

스마트 홈 (Home Kit)

이미지 처리 (Image I/O)

지도 정보 (Map Kit)

전화 기능 활용 (Mobile Core Services)

전자티켓 (Pass Kit)

보안 관련 : 키체인, 인증서 (Security)

iOS app - watch app간 통신 (Watch Connectivity)

Watch app 인터페이스 (Watch Kit) : 기존 인터페이스에서 추가로 다양한 인터페이스 제공 (미디어 파일 : 오디오/비디오 재생 등)


    특히 위에서 언급한 것 중에 Complication 기능은 사용자에게 자주 접근하는 정보를 Watch face상에 표시해줘 편의성을 증대시킨다. 서비스의 성격에 따라 Push Notification으로 정보 업데이트를 알려줄건지, Complication을 사용할건지 정해야 한다. Complication을 배치할 수 있는 유형은 아래와 같다. Complication의 구현에 대해 보다 상세한 내용을 원한다면 Apple Complications Document를 참고한다.





3. 코드 구현 상, 주의사항

Extension Delegate


    기존의 Extension은 iOS app에 귀속되어 있어 Apple watch의 life cycle과 독립적이었지만 watchOS 2에서는 watch app으로 이동하면서 Apple watch의 life cycle에 기반한 로직 구현이 가능해졌다. WKExtensionDelegate protocol을 이용하여 Apple watch life cycle에 따른 로직을 구현하자.



Managing Your Data


    Extension이 watch app으로 이동하면서 데이터 관리 영역에 있어, watch app의 역할이 커졌다. watch app에서 사용자 데이터는 'Documents' 디렉토리에 저장하고, 중요도가 낮은 임시 데이터는 'Caches' 디렉토리에 저장한다. watch app은 자동 백업 기능을 아직 지원하지 않는다. 그래서 데이터 백업을 위해서는 iOS app으로 데이터를 전송하여 저장한다. 아이클라우드에 파일을 업로드/다운로드 하기 위해서도 iOS app을 통해서 사용해야 한다. watchOS 2에서는 기존 OS버전과 달리 오디오/비디오 파일을 실행할 수 있다. 이를 위해서 미디어 파일은 Extension의 Bundle에 저장한다. 다운로드 받은 미디어 파일은 Shared Container에 저장하도록 한다.



Communicating with Companion iOS app


    watchOS 2에서는 Watch Connectivity framework를 이용하여 foreground와 background에서 데이터와 파일 전송을 할 수 있도록 한다. Watch Connectivity framework에서 WCSession으로 WatchKit extension과 iOS app간 연결을 관리한다. 그리고 session을 활성화하면 다른 앱간의 메시지를 주고 받을 수 있다. 실제로 앱간 통신을 구축하기 위해서 아래의 사항들을 참고하여 구현하면 용이하다.



watch app에 새로운 데이터를 넣을 때, 백그라운드 새로고침을 사용한다. 'updateApplicationContext: error:' 메소드를 이용한다. 특히 Glance를 주기적으로 업데이트할 때, 유용하다.


Delegate 메소드에서는 파일을 바로 옮긴다. 'session: didReceiveFile:' 메소드를 사용하여 파일을 이동할 경우, 시스템에서 불필요한 공간을 확보하기 위해 파일을 지울 수 있으므로 새로운 저장소로 옮겨 줘야 한다.


필요한 데이터만 전송한다. 불필요한 데이터를 전송하면 전력을 낭비하게 된다.

가변성 임시 데이터는 application context를 사용한다. 'updateApplicationContext: error:' 메소드는 watch app과 iOS app간 상태 정보의 스냅샷을 주고 받는 수단이다. 이 메소드를 여러 번 호출할 경우, 이전 호출과 데이터가 교차할 수 있다. 데이터의 온전한 전송을 보장하기 위해서는 'transferUserInfo:'를 대신 사용하도록 한다.

각 error 케이스에 대처한다. 데이터를 저장할 공간이 부족하여 에러가 발생할 수 있다. 그러면 불완전한 데이터로 인한 이상 동작에 대한 케이스들을 적절히 대처해야 한다.

background 데이터 전송은 즉시 실행되지 않는다. 단편적인 파일이나 데이터들은 가능한 빠르게 전달된다. 하지만 크기가 큰 파일들은 그에 상응하는 시간이 걸린다는 걸 명신해라.


Network-Based Operations


    Network Operation을 위해서 NSURLSeesion을 사용한다. NSURLSession은 iOS app에서와 같은 동일 스펙으로 지원한다. 추가로 watch app에서 데이터를 가져올 때, watch app이 언제든지 종료될 수 있음을 명심하자. 조금이라도 watch app이 종료되는 것을 방지하기 위해서는 NSProcessInfo의 'performExpiringActivityWithReson: usingBlock:'을 사용한다.


    iOS app과 watch app이 같은 서버와 통신한다면 하나의 앱에서 데이터를 동기화 해오는 것보다 각자 서버와 통신하는 것이 훨씬 간단하다. 굳이 두 앱간의 데이터를 동기화하고 싶다면 통신은 각 앱에서 따로 수행하고, 'updateApplicationContext: error:' 메소드를 활용하여 동기화 하도록 한다.


Core Spotlight framework

    Spotlight는 검색 기반의 아이폰 사용의 중심에 있다. 특히 iOS9 이후로 Siri와 spotlight의 결합으로 개인 비서로서 기능이 강화됬다. iOS9 이후 Core Spotlight라는 API를 제공함으로써 개발자는 앱을 spotlight와 결합하여 Depp Link를 구현하는 등의 앱과 OS의 결속력을 높여 더 높은 차원의 UX를 제공할 수 있게 됬다. 그럼 Core Spotlight를 이용하여 앱 데이터, 서비스를 Indexing하는 방법에 대해 알아보자.



CSSearchableItemAttributeSet

    CSSearchableItemAttributeSet은 search result에 표시하기 위한 메타데이터를 저장하는 객체이다. 각 프로퍼티의 데이터는 spotlight에서 검색 시, 결과 화면에 표시된다. CSSearchableItemAttributeSet은 다중 스레드 환경에 대비 되어 있지 않다. 그리하여 property에 데이터를 setting할 때 단일 스레드를 이용해야한다.



CSSearchableItem

    CSSearchableItem은 기기에서 사용자가 검색을 통해 사용할 수 있는 아이템을 표현하기 위해 사용하는 객체이다. Core Spotlight를 사용하기 위해서 먼저 CSSearchableItemAttributeSet을 생성하여 결과 화면에 표시할 메타데이터를 setting한다. 생성된 CSSearchableItemAttributeSet을 CSSearchableItem을 초기화할 때, 초기화 변수로 사용한다. 이렇게 만들어진 CSSearchableItem 객체를 아래에서 설명할 CSSearchableIndex에 indexing하면 기기에서 spotlight를 통해 서비스를 제공하게 된다.



CSSearchableIndex

    CSSearchableIndex는 기기에 등록된 index를 표현하기 위해 사용하는 객체이다. 'defaultSearchableIndex' 메소드를 통해 객체를 반환 받은 다음, 'indexSearchableItems:completionHandler:' 메소드를 통하여 CSSearchableItem을 index에 등록해주면 된다.




<참조>

Core Spotlight API Documents

How to use Core Spotlight in Swift

How to use Core Spotlight in Objective-C

    NSUserActivity는 검색 결과나 핸드오프를 통하여 사용자가 앱을 사용하던 상태를 저장 복원하기 위해 사용하는 간편한 방법이다. 핸드오프 기능이 처음으로 제공된 iOS8에서부터 지원한다. 처음엔 iOS8에서는 핸드오프를 지원하기 위해서 추가되었다. 그런데 iOS9부터 각 상태를 indexing하여 spotlight 검색결과로 활용할 수 있도록 개선되었다. 먼저 핸드오프에서 사용하는 방법에 대해 말하고, spotlight를 통한 indexing은 다음 포스팅에서 다루겠다.



Hand-off

    핸드오프는 Mac OS X과 iOS 기기간 지속적인 작업이 가능하도록 해주는 기능이다. 하지만 지속적인 작업을 가능하게 하기 위해서 사용자가 작업하던 환경을 저장했다가 복구하는 수단이 필요하다. 그리고 저장하고 복원하는 데이터의 저장과 전송이 가벼워 사용자 사용환경을 해치지 않고 기기간의 부하를 줄여야 한다. 이를 위해 사용하는 것이 위에서 언급한 NSUserActivity이다. 


    핸드오프 기능을 구현하기에 앞서, 핸드오프를 사용하기 위한 조건이 있다. 같은 아이클라우드 계정으로 로그인된 기기가 2개 이상이 있어야 하며 해당 기기는 모두 Bluetooth LE 4.0 규격을 지원해야한다.


    먼저, 핸드오프를 구현하기 위해서 NSUserActivity 객체를 생성한다. 생성된 NSUserActivity 객체는 UIResponder의 '-(void)updateUserActivityState:(NSUserActivity *)activity'를 오버라이딩하여 user activity를 등록한다. OS에서 주기적으로 위의 메소드를 호출하게되고 호출할 때, 개발자가 등록한 user activity를 갱신하면서 Bluetooth LE를 통해 브로드캐스팅을 한다. 브로드캐스팅한 activity는 AppDelegate 메소드인 '-(void)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:'에서 수신한다. 수신한 NSUserActivity를 사용하여 사용자의 작업 환경을 복원해 핸드오프 기능을 완성한다.



Activity Types

    각 NSUserActivity는 activityType을 가진다. 핸드오프 기능을 사용하는 각 앱은  자신이 수신해 사용할 userActivity의 activityType을 info pilist에 등록을 해야한다. info plist에 등록할 때는 'NSUserActivityTypes'라는 키를 사용하여 array로 등록한다.







    애플에서 Swift를 오픈소스화 한다는 공약을 실천했다. 현재 레파지토리는 Github를 이용하고 있으며 담당 사이트로 Swift.org를 개설했다. 이슈 트래킹은 Jira를 이용한다. Mailing-list를 통하여 Swift의 변동사항과 개발 이슈/동향 등을 reporting 받거나 다른 개발자들과 소통 창구로 이용할 수 있다. 아래 Swift 관련 URL을 모아 놨다. 역시 애플에서 관리하는 프로젝트라서 Contribution section에 있는 가이드를 꼼꼼히 읽고 접근하는 것이 좋을 것 같다.



Apple in Github : https://github.com/apple

Swift Repository : https://github.com/apple/swift


Swift.org : https://swift.org

Mailing-Lists : https://swift.org/community/#mailing-lists

Bugs in Swift with Jira : https://bugs.swift.org/secure/Dashboard.jspa

    우리는 일전에 iOS8에서 소개되었던 Interactive Notification이라는 기능에 대해 배운바가 있다. 그런데 필자가 다시 Interactive Notification이라는 물건을 다시 꺼내 들어 설명하는 이유는 무엇인가. 그토록 기다려왔던 TextField가 Interactive Notification에 추가되어 API로 공개되었기 때문이다. 아래의 이미지는 페이스북 메신저에서 선 구현한 Interactive Notification에 TextField가 추가된 모습이다. 열심히 구글링을 한 결과, iOS9에서 새롭게 추가된 프로퍼티의 영향임이 확인되었다.






    UIUserNotificationAction이라는 것을 이용하여 Interactive Notification에 사용자의 action을 활용할 수 있음을 이미 배웠다. UIUserNotificationAction에서 우리가 주목해야 할것은 behavior라는 프로퍼티이다. behavior는 iOS9에서 새롭게 추가된 프로퍼티로 'Default'와 'TextInput'이라는 2개의 상수를 택할 수 있다. 이중 TextInput을 behavior에 set하면 Notification을 처리할 때, 'UIUserNotificationActionResponseTypedTextKey'라는 키를 이용하여 NSString 객체를 획득할 수 있다. 이를 이용하여 NSString 객체를 이용한 사용자 서비스를 추가할 수 있다. 위의 이미지에서 보듯이 'Quick Reply'같은 기능을 말이다.


    아래는 StackOverflow에서 찾아낸 예제 코드이니 참조하도록 하자.


 //creating the inline reply notification action
   let replyAction = UIMutableUserNotificationAction()
   replyAction.title = "Say Something"
   replyAction.identifier = "inline-reply"
   replyAction.activationMode = .Background
   replyAction.authenticationRequired = false
   replyAction.behavior = .TextInput

 //creating a category
   let notificationCategory:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
   notificationCategory.identifier = "INVITE_CATEGORY"
   notificationCategory .setActions([replyAction], forContext: UIUserNotificationActionContext.Default)

 //registerting for the notification.
      application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes:[ UIUserNotificationType.Sound, UIUserNotificationType.Alert,
            UIUserNotificationType.Badge], categories: NSSet(array:[notificationCategory]) as? Set<UIUserNotificationCategory>))


SFSafariViewController


SFSafariViewController는 iOS9부터 지원한다. 기존의 UIWebView와 달리 브라우징 화면만 존재하는 것이 아니라, 기본 인터페이스(읽기 도구, Full Browsing, Content Blocking 등)가 포함된다. 그리고 번들앱인 Safari와 cookie와 웹 사이트 데이터 등을 공유한다. 앱에서 SFSafariViewController와 사용자 사이의 데이터(히스토리, 웹사이트 데이터 등)를 가로챌 수 없다. SFSafariViewController는 아래와 같은 UI적 특징을 가진다.


- '읽기 도구' 버튼을 제공

- 공유나 커스텀 서비스 이용에 필요한 액션 버튼을 제공

- Back/Forward 버튼을 제공하며, 현재 페이지를 Safari에서 열수 있는 버튼도 제공

- 3D Touch가 지원되는 기기에서 웹사이트 데이터를 자동으로 분석하여 Pick/Pop 기능을 제공




SFSafariViewControllerDelegate


SFSafariViewController의 생명주기에 관한 주요 이벤트에 대한 Delegation을 구현 가능하다. 주요 이벤트는 아래와 같다.


- Initial  load가 완료되었을 때

- UIActivityViewController에 포함한 특정 Application service를 선택했을 때

- SFSafariViewController가 완료되어 View에서 사라질 때

+ Recent posts