2021년 6월 3일 목요일

OOP 설계 사례

 객체지향이라는 종교에 가까운 프로그래밍 방법론에 대해서는 이게 정답이냐 하는 논쟁거리는 뒤로 하고(https://samse.tistory.com/entry/Goodbye-OOP 참고) 일반적으로 어떤 기능 또는 자연계의 현상을 컴퓨터가 이해할 수 있도록 사례를 분석하고 일반화하고 그에 따라 컴퓨터와 통하는 언어로 표현하는 방법에 대해서 지금까지 내가 책을 통해서 배우고 실무를 통해서 실제로 사용해 왔던 나만의 사례를 기술해보고자 한다.


사용자의 스마트폰에 앱을 설치하여 위치정보를 관제센터에서 실시간으로 모니터링하고자 하는 고객이 있었다.


고객의 요구사항은 대체로 이러하다.


1. 위치정보를 주기적으로 수집하여 서버에 전송해야 한다.

2. 앱을 사용중이 아닐때에도 동작하여야 한다.


위와 같은 요구사항을 기반으로 제일먼저 진행해야 하는 작업은 요구사항의 구체화이다.

모바일단말에서 동작하므로 모바일환경에서 가능한 방법과 만일 불가한 경우 이를 보완할 방법등이 있는지를 검토하고 고객과 논의하여 최종 요구사항을 확정지어야 한다. 

대게의 고객은 "이거 다 되어야 하는거 아니에요?", "요구사항에 있잖아요?" 

이런 반응을 나타내는 경우가 왕왕 있다.


위 사례의 경우는 몇가지 짚고 넘어가야 하는 케이스가 있다.


1. 위치정보는 개인정보에 해당하므로 위치정보 서비스 사업자 등록을 해야 한다.

2. 위치정보를 사용하기 위해 사용자로부터 권한을 획득해야 하며 획득하지 못하면 해당 기능을 사용할수 없다.

3. 앱 사용중이 아닌 경우 백그라운드로 동작하려면 안드로이드의 경우 상태바에 알림박스가 떠야 한다.


이정도는 미리 짚어주면 좋겠다.


다음으로 해야 할 작업은 브레인스토밍이다. 일명 마인드맵이다(여러가지 마인드맵 앱이 있지만 적당한것을 찾지 못하였다. 결국 그냥 노트에 그리게 되었다.)

마인드맵은 머리속의 생각을 구체적으로 표현할 수 있고 이를 통해 분석해나가기 아주 좋은 툴이다.


위 요구사항을 기반으로 생각나는데로 계속 표현해 나간다.


위치권한 획득

위치정보데이터 모델 -> 서버 API기반으로 필요한 정보를 정의

위치추적 작업 스레드 -> 권한체크 후 시작 -> 권한 없으면 실패 콜백 -> 실패원이 화면에 표시

위치정보 저장 및 서버 전송 -> 바로 전송하지 않음. ->네트웍 상태에 따라 지연이 발생할 수 있음

저장 및 전송은 별개의 스레드로 동작해야 함.

저장은 DB로 -> SQLite? Realm?

서버 API -> 서버팀에 요청


글로 적었지만 위 내용은 마인드맵 규칙에 따라 계층적으로 생각의 흐름대로 표현해 나간다.

그리고 멀찍히 서서 그림을 본다.

그러다 보면 마치 매직아이처럼 몇가지 그룹으로 끼리 끼리 군집을 이룰 수 있는것을 발견하게 된다. 이 부분은 순전히 경험에 기인하는 면이 있지만 유사한것들 끼리 또는 구별이 되는것들을 나누어 보는것은 누구나 할 수 있을 것이다.



1. 백그라운드 위치추적 서비스

2. 위치정보 저장소

3. 위치정보 서버전송

4. 수집정보 데이터 모델

5. 권한 체크

6. 서버전송 네트웍 관리자


대충 느끼겠지만 위에 나열한것들은 클래스에 해당하게 된다.


1. TrackingService

2. TrackingDataStorage

3. TrackingSyncService

4. TrackingData

5. PermissionHelper

6. NetworkHelper


각 클래스별로 해야하는 작업은 다음과 같다.


1. TrackingService

 - 위치권한확인

 - 위치추적 시작, 중지

 - 백그라운드에서 동작

 - 추적된 정보를 저장소에 저장 및 서버 전송

2. TrackingDataStorage

 - 위치정보를 DB에 set, get, remove

3. TrackingSyncService

 - 저장소의 데이터를 읽어서 전송

 - 전송된 데이터는 삭제

4. TrackingData

 - 수집된 위치정보 데이터 클래스

 - GPS위치정보(위도, 경도, 고도)와 사용자정보나 수집시간등 필요한 정보를 추가

5. PermissionHelper

 - 위치권한 체크

 - 위치권한 요청

6. NetworkHelper

 - 저장소에서 데이터 읽어서 전송 후 삭제


절반정도 설계가 완료되었다.

2020년 7월 27일 월요일

CEF 개요 및 튜터리얼

CEF

 

Chromium Embedded Framework (CEF).

chromium 기반의 브라우저를 내포하는 앱을 만들 수 있는 프레임워크이다.

 

 

BSD-license이고 구글 크로미엄프로젝트 기반으로 2008년에 마샬그린브랫에의해 시작된 오픈소스 프로젝트이다.

크로미엄프로젝트와 다르게 구글 크롭앱개발에 주로 집중하였고 써드파티앰의 내장된 브라우저 사용 케이스에 집중하였다.

CEF는 크로미텀의 하부와 코드의 복잡함으로부터 고수준의 안정화된 API, 특정목적의 배포, 그리고 바이너리 배포를 통해 벗어날수 있게 해준다.

CEF기본 구현은 사용자로부터 조금의 또는 전혀 통합하는 작업을 필요로 하지 않는다.

현재 1억개 이상의 인스턴스가 사용되고 있다. 그 일부가 위키페이지에 정리되어 있다.

 

사용케이스 몇가지는 다음과 같다.

* 기존 네이티브 애플리케이션에 HTML5호환 웹브라우저를 내장

* 가벼운 네이트브 쉘애플리케이션으로 기본적으로 웹기술에 기반하여 개발된 UI를 호스팅

* 자체적인 드로잉프레이뭐크를 사용하는 오프스크린 웹컨텐츠를 렌더링

* 웹속성이나 애플리케이션의 자동화된 테스트를 수행

 

CEF는 수많은 언어와 운영체제를 지원하며 새로운 또는 기존애플리케이션에 쉽게 통합이 가능하다. 성능과 쉬운 사용을 목표로 설계되었다.

기반프레임워크는 C, C++로 네이티브라이브러리로 제공되고 애플리케이션과 크로미엄과 상세한 구현으로부터 분리해준다.

이는 브라우저와 커스텀플러그인, 프로토콜, 자바스크립트 객체 그리고 자바스크립트 확장 등을 포함하는 앱간에 밀접한 통합을 제공한다.

애플리케이션은 리로스로딩을 제어하거나, 네비게이션, 메뉴, 프린트 등을 하면서 구글 크롬웹브라우저와 동일한 성능과 HTML5기술을 사용할 수 있다.

 

수많은 개인과 단체가 시간과 자원을 CEF개발에 기연하고 있지만 더 많은 커뮤니티로부터의 참여는 항상 환영한다.

 

시작하기

 

시작은 튜터리얼 위키페이지에서 CEF 사용에 대한 개요를 읽고 아키텍쳐적인 내용이나 사용이슈에 대한 더 깊은 논의를 하기 위하여 GeneralUsage 위키를 읽어라.

API문서를 살펴보고 지원과 관련된 논의에 대해서는 포럼을 참고하라

 

 

 

CEF Tutorial

 

CEF3로 간단한 앱을 작성하는 방법을 설명한다. CEF3 사용에 대해서는 GeneralUsage위키페이지를 참고하라.

 

시작하기

 

CEF개발을 시작하기 쉽게 해주는 샘플프로젝트를 제공한다. cef-project 웹사이트에 방문하고 단계별로 설명을 따라해보기 바란다.

 

 

커스텀URL 로딩하기

 

샘플프로젝트는 구글 홈페이지를 로딩한다. 다른 url로 변경하려면 커맨드라인에서 다음과같이 수행한다.

 

# Load the local file “c:\example\example.html”

cefsimple.exe --url=file://c:/example/example.html

 

샘플프로젝트에서 cefsimple/simple_app.cc에서 다음라인을 수정하고 다시 컴파일하면 바로 진입한다.

 

// Load the local file “c:\example\example.html”

if (url.empty())

  url = "file://c:/example/example.html";

 

 

애플리케이션 요소들

 

모든 CEF애플리케이션은 다음의 기본 콤포넌트들로 구성된다.

 

1. CEF dynamic library(libcef.dll 윈도우용, libcef.so 리눅스용, Chromium Embedded Framework.framework 맥용)

2. 지원 파일들(*.pak, *.bin, ...)

3. 리소스 (html/js/css, string, ...)

4. 클라이언트 실행파일

 

CEF dynamic library, 지원파일들, 리소스는 모든 CEF 애플리케이션에 존재한다. 바이너리 배포판의 Debug / Release 또는 Resources 디렉토리에 포함된다. 이러한 파일 중 필요한 파일과 제외 할 수있는 파일에 대한 자세한 내용은 이진 배포에 포함 된 README.txt 파일을 참조하십시오. 각 플랫폼에서 필요한 애플리케이션 레이아웃에 대한 자세한 설명은 아래를 참조하십시오.

 

60초 아키텍쳐

 

다음은 이 튜터리얼의 기본적인 중요한 아이템들을 요약한다.

 

* CEF는 멀티프로세스를 사용한다. 메인 애플리케이션 프로세스는 '브라우저' 프로세스로 불린다. 서브프로세스들은 렌더러, 플러그인, GPU등으로 생성해되게 될것이다.

* 윈도우와 리눅스에서는 메인과 서브프로세스가 같이 실행될 수 있다. OS-X는 실행과 서브프로세스의 번들이 분리되어야 한다.

* 대부분의 프로세스는 다중스레드를 갖는다.

* 일부 콜백과 함수들은 개개 프로세스 또는 개개의 스레드를 사용할 수 있다. 콜백이나 함수를 처음 사용하는 경우 헤더파일의 코멘트를 반드시 읽어볼것을 권장한다.

 

소스코드

 

cefsimple애플리케이션은 CEF를 초기화하고 하나의 브라우저팝업윈도우를 생성한다. 애플리케이션은 모든 브라우저윈도우가 닫히면 종료된다. 프로그램 흐름은 다음과 같다.

 

1. OS는 브라우저프로세스를 시작한다. 진입포인트는 main또는 wWinMain

2. 진입포인트 함수는

  프로세스레벨의 콜백을 처리하는 SimpleApp의 인스턴스를 생성한다.

  CEF를 초기화 하고 CEF message loop를 시작한다.

3. CEF가 초기화 되면 SimpleApp::OnContextInitialized가 호출된다. 이 메서드는

  SimpleHandler의 싱글턴 인스턴스를 생성한다.

  CefBrowserHost::CreateBrowser를 사용하여 브라우저윈도우를 생성한다.

4. 모든 브라우저는 SimpleHandler인스턴스를 공유하며 이는 브라우저의 행위를 커스텀하고 관련된 콜백을 처리한다(life span, 로딩상태, 타이틀 표시등...)

5. 브라우저하나가 닫히면 SimpleHandler::OnBeforeClose가 호출된다. 모든 브라우저가 닫히면 CEF message loop가 끝나며 애플리케이션이 종료된다.

 

 

진입포인트 함수

 

이 함수는 CEF와 운영체제 관련된 객체들을 초기화해야 한다. 예를 들어 리눅스에서는 X11 에러헨들러를 인스톨해야 하고, OS-X에서는 필요한 코코아객체들의 할당이 필요하다.

 

* 윈도우 : cefsimple/cefsimple_win.cc

* 리눅스 : cefsimple/cefsimple_linux.cc

* OS-X

  브라우저 프로세스 : cefsimple/cefsimple_mac.mm

  서브 프로세스 : cefsimple/process_helper.mac.cc

 

 

SimpleApp

 

프로세스레벨의 콜백을 처리해야 한다. interface/method로 노출되며 멀티프로세스에 의해 공유되고 각 프로세스만 에서 호출된다.

예를 들어 CefBrowserProcessHandler인터페이스는 브라우저 프로세스에서만 호출된다.

CefRenderProcessHandler인터페이스(샘플에서는 없음)는 렌더프로세스에서만 호출된다.

GetBrowserProcessHandler는 반드시 this를 리턴해야 한다. SimpleApp CefApp CefBrowserProcessHandler를 구현해야 하기 때문이다. GeneralUsage 위키페이지나 API 헤더파일을 보고 CefApp과 관련된 인터페이스들에 대해 좀더 많은 정보를 살펴보길 바란다.

 

* 공유된 구현 : cefsimple/simple_app.h, cefsimple/simple_app.cc

 

SimpleHandler

 

브라우저레벨의 콜백을 처리해야 한다. 브라우저 프로세스로부터 실행된다. 샘플에서우리는 CefClient인스턴스를 모든 브라우저에서 사용했다. 하지만 당신의 애플리케이션은 각각 전용의 인스턴스를 사용해도 된다. GeneralUsage 위키페이지나 API 헤더파일을 보고 CefApp과 관련된 인터페이스들에 대해 좀더 많은 정보를 살펴보길 바란다.

 

* 공유된 구현 : cefsimple/simple_handler.h, cefsimple/simple_handler.cc

* 윈도우 : cefsimple/simple_handler_win.cc

* 리눅스 : cefsimple/simple_handler_linux.cc

* OS-X : cefsimple/simple_handler_mac.mm

 

빌드 순서

 

빌드 순서는 플랫폼에 의존한다. binary 배포와 함께 포함된 CMake파일을 찾아서 살펴보면 모든과정에 대한 이해가 가능할것이다. 모든 플랫폼에 공통된 빌드 순서는 일반적으로 다음과 같이 요약될 수 있다.

 

1. libcef_dll_wrapper static library를 컴파일

2. application 소스 컴파일, libcef dynamic library libceff_dll_wrapper static library 링크

3. library와 리소스들을 출력디렉토리에 복사

 

 

윈도우 빌드순서

 

1. libcef_dll_wrapper static library를 컴파일

2. cefsimple.ext 컴파일 및 링크

- 필요한 소스코드 : cefsimple_win.cc, simple_app.cc, simple_handler.cc, simple_handler_win.cc.

- 필요한 라이브러리 : comctl32.lib, shlwapi.lib, rcprt4.lib, libcef_dll_wrapper.lib, libcef.lib, cef_sandbox.lib

cef_sandbox.lib(sandbox지원시) VS 2015 update3으로 빌드된 정적라이브러리이다. 다른 버전의 VS는 컴파일이 안될수도 있다. sandbox를 지원하지 않으면 문제가 없으며 cefsimple_win.cc의 코멘트를 확인하여 비활성화 하면 된다.

- 리소스 파일 : cefsimple.rc

- 매니페스트 파일 : cefsimple.exe.manifest, compatibility.manifest

3. Resource 폴더의 모든 파일 대상폴더로 복사

4. Debug/Release 폴더의 모든 파일 대상폴더로 복사

 

결과 폴더의 구조는 2526 브랜치정도가 보일거다.

 

Application/

    cefsimple.exe  <= cefsimple application executable

    libcef.dll <= main CEF library

    icudtl.dat <= unicode support data

    libEGL.dll, libGLESv2.dll, ... <= accelerated compositing support libraries

    cef.pak, devtools_resources.pak, ... <= non-localized resources and strings

    natives_blob.bin, snapshot_blob.bin <= V8 initial snapshot

    locales/

        en-US.pak, ... <= locale-specific resources and strings

 


2020년 7월 20일 월요일

iOS App Extension #3 만들기

앱익스텐션을 개발할 준비가 되었다면 먼저 적절한 익스텐션포인트를 결정해야 한다. 원하는 익스텐션포인트에 해당하는 Xcode 익스텐션 템플릿으로 시작하여 코드와 UI를 커스텀하는 방식으로 진행하는것이 좋다. 테스트와 최적화가 완료된 후 포함하는 앱에 함께 배포할 준비가 된것이다.

적절한 익스텐션포인트를 선택하여 개발 시작하기

익스텐션포인트가 잘 정의되어 있기 때문에 이를 선택하는것이 우선이다. 이를 선택함으로써 사용가능한 API와 동작이 결정된다.

익스텐션포인트는 iOS, OS X에서 제공되고 Info.plist에 익스텐션포인트 아이디키로 정의되어 있으며 다음 링크에서 확인이 가능하다.

익스텐션포인트를 선택하면 새로운 타겟을 추가한다. 가장 쉬운 방법은 기본설정을 모두 가지고 있는 Xcode template을 사용하여 추가하는 방법이다.

Xcode프로젝트에 타겟을 추가하려면 File > New > Target를 선택. Application Extension for iOS 또는 macOS 를 선택. 그러면 다음 그림에서처럼 선택가능한 템플릿들이 나온다. 



템플릿을 선택하면 익스텐션코드를 수정하기 전에 빌드하고 실행해볼수 있다. 빌드가 완료되면 .appex로 끝나는 익스텐션번들을 얻을 수 있다.

대부분 디폴튼 익스텐션을 시스템프리퍼런스나 셋팅에서 활성화함으로써 다른앱을 통해서 접근하여 테스트가 가능하다. 예를 들어 OS X extension을 사파리에서 웹페이지를 오픈하고 툴바의 공유버튼을 선택할 수 있고 이 때 나타나는 당신의 익스텐션을 선택 가능하다.

디폴트익스텐션 템플릿 살펴보기

각 익스텐션템플릿은 Info.plist, 하나의 view controller클래스 그리고 기본 UI를 포함하고 있으며 이것들은 익스텐션포인트에서 정의된 것들이다. 기본 view controller 클래스(또는 기반클래스)는 반드시 구현해야 하는 필수 메서드들을 가질 수 있다.

앱익스텐션포인트 타겟의 Info.plist에 익스텐션에 대한 상세한 정보를 포함할 수 있다. NSExtension키가 기본으로 있으며 Dictionary로 키/값쌍으로 익스텐션포인트를 기술한다. 예를 들어 NSExtensionPointIdentifier 키는 익스텐션포인트의 리버스 DNS명이다. 다음은 NSExtension 딕셔너리에서 볼 수 있는 키/값들이다.

- NSExtensionAttributes 익스텐션포인트에 해당하는 속성들의 딕셔너리로 PhotoEditing익스텐션의 경우 PHSupportedMediaTypes가 예이다.
- NSExtensionPrincipalClass 템플릿에서 생성하는 기본뷰컨트롤러 클래스의 이름으로 SharingViewController같은 호스트앱이 익스텐션을 호출할때 이 클래스를 인스턴스화하게 된다.
- NSExtensionMainStoryboard(iOS 익스텐션만 해당)  기본 스토리보드파일, MainInterface로 보통 명명됨

Info.plist외에 일부 capability가 추가될 수 있다. iOS Document Provider익스텐션의 경우 com.apple.security.application-groups 인타이틀먼트를 포함한다.

모든 OS X 앱익스텐션 템플릿들은 App Sandbax와 com.apple.security.files.user-selected.read-only 인타이틀먼트를 기본으로 포함한다. 익스텐션이 네트웍이나 연락처의 사진에 접근한다면 추가적인 capability  를 포함해야 한다.


 호스트앱의 요청에 응답하기.

호스트앱에서 유저가 익스텐션을 선택하면 익스텐션이 오픈되고 호스트앱의 요청이 발생한다. 고수준에서는 익스텐션은 이 요청을 받게되며 유저의 작업을 수행하고 사용자의 액션에 따라 요청을 완료하거나 취소한다. 
공유익스텐션의 경우 호스트앱의 요청을 받아서 표시된 뷰에서 응답한다. 유저가 내용을 구성하고 포스트 전송 또는 취소를 선택하면 익스텐션은 완료 또는 취소를 처리한다.

호스트앱이 요청을 전달하면 거기에는 익스텐션의 컨텍스트를 기술한다. 많은 익스텐션에서 익스텐션 컨텍스트에서 중요한 부분은 익스텐션에서 원하는 작업에 대한 아이템을 기술하는 것이다. 예를 들어 OS X 공유 익스텐션의 컨텍스트는 유저가 포스팅하는데 필요한 선택된 테스트가 될것이다.

호스트앱의 요청이 발생하면 앱익스텐션은 기반 뷰컨트롤러의 extensionContext프로퍼티를 컨텍스트를 얻기 위해 사용할 수 있다. 자식뷰컨트롤러 역시 체이닝을 통해 이 프로퍼티에 접근 할 수 있다.

이어서 NSExtensionContext클래스로 컨텍스트의 아이템을 사용할 수 있다.  뷰컨트롤러의 loadView메서드에서 컨텍스트와 아이템에 접근할 수 있으며 이 정보를 뷰에 표시할 수 있다. 다음과 같이 이 작업을 수행할 수 있다.

  1. NSExtensionContext *myExtensionContext = self.extensionContext;
컨텍스트 객체의 inputItems에 익스텐션에서 필요한 아이템들이 있다. NSExtensionItem객체의 배열을 답고 있으며 다음과 같이 사용 가능하다.

  1. NSArray *inputItems = myExtensionContext.inputItems;
각 NSExtensionItem객체는 아이테믜 유형을 설명하는 여러개의 프로퍼티(title, content text, attachment, userInfo등)를 담고 있다. 

attachments 프로퍼티는 아이템과 관련된 미디어데이터 배열을 포함한다. 공유요처어의 경우 유저가 공유하고자 하는 웹페이지의 표현이 포함될수 있다.

input items 으로 작업 후 보통 유저에게 테스크를 완료 혹은 취소를 선택하도록 한다. 유저의 선택에 따라 completeRequestReturningItems:completionHandler: 메서드를 호출하는데 NSExtensionItem객체를 추가로 반환할 수 있고 cancelRequestWithError:메서드로 에러코드를 반환할 수 있다.

iOS에서 앱익스텐션이 잠재적으로 긴 작업을 완료하기 위해 긴 시간이 필요할 수 있다. 웹사이트에 컨텐츠를 업로드할 때 가 해당된다. 이럴경우 NSURLSession클래스로 백그라운드에서 전송을 시작할 수 있다. 백그라운드 전송이 ㅂ ㅕㄹ도의 프로세스를 사용하기 때문에 전송은 계속될 수 있고 낮은 우선순위의 테스크로 처리되고 익스텐션이 완료된 후 종료된다.

업로드/다운로드 이외의 VoIP, 오디오 백그라운드 재생같은 백그라운드 작업은 불가하다. 만일 UIBackgroundModes키를 익스텐션의 Info.plist에 기술한다면 AppStore에서 리젝될것이다.

효율성 및 성능 최적화

앱익스텐션은 유저에게 빠르고 가벼운 느낌을 주어야 한다. 1초이내의 빠르게 적재되도록 앱익스텐션을 설계해야 한다.

포그라운드앱의 메모리제한보다 앱익스텐션은 훨씬 더 적게 사용해야 한다. 두 플랫폼에서 모두 시스템은 유저가 호스트엡의 주목적에 맞게 돌아가기를 원하기 때문에 익스텐션을 적극적으로 종료할것이다. 일부 익스텐션은 더 적은 메모리를 사용한다. 예로 위젯은 동시에 유저가 몇개의 위젯을 열수도 있기 때문에 더 효율적이어야 한다.

앱익스텐션은 자신의 메인 루프를 가지고 있지 않기 때문에 메인루프에서 적절한 동작을 하려면 설정된 룰을 따라야 한다. 예로 익스텐션이 메인루프를 블럭한다면 이는 다른 익스텐션이나 앱에세 좋지않은 사용자경헙을 선사하게 될것이다.

GPU는 시스템에서 리소스를 공유한다는 것을 상기해야 한다. 공유리소스에 대해 앱익스텐션은 우선순위가 낮다. 예로 투데이 위젯이 그래픽이 강조된 게임에서 사용된다면 아주 나쁜 결과를 초래할것이다. 메모리가 부족해지면 시스템은 이런 익스텐션을 죽이려고 한다.  시스템리소스를 많이 사용하는 기능은 앱이 적합하며 앱익스텐션은 그렇지 않다.

간소화된 UI설계

대부분의 앱익스텐션은 당신의 익스텐션을 오픈할때 보게되는 커스텀UI를 제공해야 한다. UI는 단순하고 억제되고 단일작업을 촉진하는데 집중해야 한다. 성능과 UX를 개선하고자 한다면 익스텐션이 제공하는 기능을 지원하지 않는 추가적인 UI는 피해야 한다.

Xcode 앱익스텐션 템플릿은 시작하는데 필요한 UI를 제공한다.

아이콘은 일관성과 가독성을 위해 포함하는 앱과 동일한게 좋다. 

액션익스텐션은 포함하는 앱의 아이콘의 이미지버전을 사용한다. 반드시 제공되어야 한다.

공유익스텐션은 자동으로 포함하는 앱의아이콘을 갖는다. 공유익스텐션 타겟에 다른 아이콘을 제공해도 Xcode는 이를 무시한다. 
나머지 다른 익스텐션들도 앱의 아이콘을 제공해야 한다.

앱익스텐션 이름은 짧고 가독성좋은 이름이어야 하며 포함하는 앱의 이름을 포함해야 한다. 패턴은 <포함하는앱이름>-<앱익스텐션이름>이다. 이는 유저가 시스템에서 쉽게 다룰수 있게 해준다. 옵션으로 포함하는앱이름을 사용해도 되는 경우가 있는데 이 경우는 오직 하나의 익스텐션만 제공하는 앱의 경우이다.

앱익스텐션이름은 타겟의 CFBundleDisplayName값이다. 이 값을 지정하지 않으면 CFBundleName값이 사용되며 포함하는 앱이름이 된다.

로컬화된 앱익스텐션으로 다국어가 적용된 앱익스텐션이름을 제공할 수 있다.

일부 앱익스텐션은 짧은 설명글이 필요할 수 있는데 OS X 위젯의 경우 투데이 뷰에서 보기를 원하는 위젯을 선택하는데 도움을 준다. InfoPlist.strings 파일에서 widget.description키의 값을 편집하면 된다.

모든 디바이스에서 iOS 앱익스텐션 지원하기

universal을 반드시 지원해야 한다. 아이폰, 아이팟터치, 아이패드등에서 모두 가능해야 한다. 포함하는 앱이 어떤 디바이스를 타겟으로 하는것과는 무관하다. Xcode 앱익스텐션 템플릿은 유니버셜로 설정되어 있다.

Xcode 빌드설정에서 디바이스패밀리를 "Universal"로 지정해야 한다.


AutoLayout과 size클래스를 UI 디자인시 적용한다. 그러면 모든 디바이스 사이즈와 방향에 대응될것이다. 


디버그, 프로파일링 그리고 테스트

앱익스텐션디버그는 다른 프로세스 디버깅하는 방법과 한가지만 제외하고 아주 비슷하다. 앱익스텐션의 Run phase에서 실행파일로 호스트앱을 지정한다. 지정한 호스트앱의 UI로 앱익스텐션에 접근하면 Xcode 디버거가 익스텐션에 연결한다.

Xcode 앱익스텐션 템플릿의 scheme에서 Ask On Launch 옵션이 사용된다. 이 옵션을 사용하면 앱을 빌드하여 실행할 때 마다 어떤 호스트앱을 사용할지 지정할 수 있다. 이렇게 하지 않고 개별적으로 호스트를 지정하고 싶다면 scheme editor에서 info tab의 app extension scheme's Run phase를 이용한다.

다음은 앱익스텐션에 Xcode debugger를 연계하는 절차이다.

1. Produce > Scheme > MyExtensionName 또는 toobar의 MyExtensionName 의 scheme 팝업메뉴 선택
2. 지정한 호스트앱을 시작하기 위해 Build and Run 버튼 선택
3. 호스트앱의 UI에서 앱익스텐션 호출하면 Xcode 디버거가 앱익스텐션의 프로세스에 연결되고 브레이크포인트가 활성화되고 익스텐션이 시작된다. 그러면 다른 프로세스의 디버깅할떄와 동일하게 디버깅 가능하다.

디버깅전에 익스텐션 scheme이 선택되어 있는지 확인해야. 앱의 scheme을 선택하면 앱익스텐션 디버깅이 아니고 앱디버깅이다.

OS X에서 호스트앱 테스트 및 디버깅으로 접근하기 전에 앱익스텐션활성화 단계를 수행해야 한다. 시스템 프리퍼런스의 익스텐션 페인에서 대부분의 익스텐션타입을 활성화 하라. 공유나 액션메뉴의 More를 선택하여 익스텐션 페인을 오픈할수도 있다.

OS X 투데이위젯을 위해 위젯시뮬레이터로 테스트 및 디버깅할 수 있다.

iOS에서 커스텀 키보드를 위해 Settings > General > Keyboard > Keyboards에서 활성화해야 한다.

OS X 디버깅 세션 동안에 Xcode는 빌드된 앱익스텐션을 등록한다. 이는 개발버전을 OS X에 설치하려면 FInder로 빌드위치에서 애플리케이션폴더같은 위치로 복사해야 한다. 

Xcode 디버깅콘솔로그에서 앱익스텐션 바이너리는 CFBundleDisplayName프로퍼티값 대신에 CFBundleIdentifier프로퍼티값이 사용된다.

앱익스텐션이 효율적이고 응답성이 있어야 하기 때문에 앱익스텐션 동작중에 디버그네비게이터의 디버그게이지를 살피는것이 좋다. 디버그 게이지는 CPU, 메모리, 다른 시스템리소스들의 사용량을 볼수 있게 해준다. 성능이슈의 증거를 보게 된다면 앱익스텐션을 프로파일하기 위해 instrument를 사용할 수 있고 향상된 부분을 확인할수도 있다. 

앱배포

포함하는 앱없이 스토어에 앱익스텐션을 배포할수 없으며 다른앱으로 이전할수도 없다.

iOS 앱익스텐션을 배포하려면 포함하는 앱을 스토어에 배포해야 한다.
OS X 앱익스텐션을 배포하려면 스토어에 포함하는 앱을 배포하는것이 추천되나 필수는 아니다.

Mac App Store가 아닌 다른 채널로 OSX 앱익스텐션을 배포하면 Getekeeper가 유저가 오픈할 때 걸러내게 되며 포함하는 앱을 승인한다. 또한 개발자 ID외의 인증서로 서명된 경우 사용자는 Gatekeeper를 명시적으로 재정의하여 익스텐션을 사용할수 있도록 포함앱을 열어야 한다.

리뷰를 통과하려면 포함하는앱은 유저에게 기능을 제공해야 한다. 앱익스텐션만 제공할수는 없다.

iOS App Extension #2 동작 이해하기


앱익스텐션은 앱이 아니며 제한되고 잘 범위지어진 정책으로 정의된 각각 익스텐션포인트에 맡게 구현된다.

앱익스텐션 생명주기

앱이 아니기 때문에 생명주기나 환경이 앱과 다르다. 대개는 뷰컨트롤러에서 표시된 앱의 UI로부터 유저에게 선택되어졌을때 시작된다. 앱익스텐션을 선택하여 실행해주는 앱을 호스트앱이라고 칭한다. 호스트앱은 앱에 제공되는 컨텍스트를 제공하며 유저의 액션의 응답으로써 리퀘스트가 전달될 때 익스텐션의 생명주기가 시작된다. 그리고 호스트앱으로부터 전달받은 요청을 모두 처리하게 되면 익스텐션도 종료된다.

예를 들어 OS X 호스트앱에서 텍스트일부를 선택했다고 상상해보자, 공유버튼이 활성화 되고 텍스트를 공유할 수 있는 공유목록에서 익스텐션하나를 선택한다. 호스트앱은 유저의 선택에 응답으로 선택한 테스트를 포함하는 요청을 앱익스텐션에 전달하게 된다. 이 상황에 대한 일반화한 다음 그림을 참고하라.



그림의 step2에서 시스템은 호스트앱의 요청을 포함하는 앱익스텐션의 인스턴스를 만들고 서로간의 통신채널을 셋업한다. 익스텐션은 호스트앱의 컨텍스트내에서 표시되어지고 작업을 수행하기 위한 호스트앱 요청을 수신한다(여기서는 선택한 텍스트이다).

그림의 step3에서 유저는 수행하거나 취소할 수 있고 닫을수도 있다. 이 액션의 응답으로 익스텐션은 유저의 작업을 바로 수행함으로 써 호스트앱의 요청을 완료하거나 필요하다면 백그라운드 프로세스로 수행한다. 호스트앱은 익스텐션뷰를 떼어내게 되고 유저는 이전 컨텍스트로 돌아가게 된다. 익스텐션의 작업이 끝나면 바로 또는 후에 결과가 호스트앱에 반환될것이다.

step4처럼 앱익스텐션의 작업을 수행한 후 시스템은 익스텐션을 종료한다.


앱익스텐션 통신

앱익스텐션은 기본적으로 호스트앱과 통신한다. 그리고 트랜잭션프로세싱처럼 동작한다. 호스트앱에서 요청을 보내고 앱익스텐션에서 응답을 받는다.다음 그림에서 호스트앱에서 시작된 실행중인 익스텐션과 이를 포함하는 앱간의 관계를 보여준다.



앱익스텐션과 이를 포함하는 앱간에 직접적인 통신은 없다 포함하는 앱은 포함된 익스텐션이 수행중에 실행되지 않는다. 포함하는 앱은 호스트앱과 통신하지 않는다.

일반적인 요청/응답의 트랜잭션처럼 시스테메서 호스트앱대신에 앱익스텐션을 오픈하며 호스트에서 제공하는 익스텐션컨텍스의 데이터를 전달한다. 익스텐션은 UI를 표시하고 작업을 수행한다. 그리고 익스텐션의 목적에 부합하는 데이터를 호스트에 반환한다.

그림에서 점선은 익스텐션과 포함하는 앱간에 유효한 상호작용이 제한된다는것을 의미한다. 투데이위젯은 시스템에 포함하는 앱의 오픈을 요청할 수 있는데 NSExtensionContext 클래스의 openURL:completionHandler: 메서드로 가능하다.

아래 그림에서 Read/Write 화살표를 보면 앱익스텐션과 포함하는 앱은 private으로 정의된 shared container에 공유된 데이터에 접근할 수 있다.



앱익스텐션에서 할수 없는 것들

시스템에 그 책임이 집중되기 때문에 앱익스텐션은 일부 작업은 불가하다. 아래 나열한것들은 수행 불가하다.

- sharedApplication 객체에 접근 불가하여 이 객체의 모든 메서드사용불가
- NS_EXTENSION_UNAVAILABLE 메크로, 잘알고 있는 메크로 또는 유효하지 않는 프레임워크등의 사용 불가, 예) HealthKit, EventKit UI framework등
- 카메라, 마이크 사용불가
- 오랫동안 동작하는 백그라운드테스크 사용불가. (NSURLSession을 이용한 upload/download를 일으킬수 있고 포함하는 앱에 결과를 보고할 수 있다)
- AirDrop으로 데이터 수신할수 없음(UIActivityViewController로 데이터를 보내는것은 가능)

2020년 7월 14일 화요일

iOS 앱 서명은 너무 어려워...

iOS 앱을 개발하여 빌드하고 테스트하여 배포하기 까지의 전체과정을 다 해본 개발자라면 애플개발자 콘솔의 잦은 변경과 앱서명 관련하여 간혹 실패하는 경우 수많은 고민을 거쳐 스택오버플로를 통해서 문제를 해결하고는 할것이다.

결국 경험이 재산이 되는데 딱히 정리가 잘 되어 있는 문서를 찾지 못하여 내가 아는 범위에서 정리를 좀 하려고 한다.

이게 개념이 잡히지 않으면 개발환경 설정 시 마다 고생하기 마련이다.





일단 기본적으로 애플은 앱실행권한을 철저하게 관리를 하고 있다. 당연하게도 이 권한이 획득되지 못하면 앱이 실행이 될수 없다.


제일먼저 해야 할 일은 애플로부터 인증서를 발급받아야 한다. 그리고 단말에서 실행을 하려면 프로비저닝이 단말에 설치가 되어야 한다.

프로비저닝은 단말과 애플인증서의 연결해주는 역할을 하며 권한을 획득하여 얻어지면 앱이 실행이 된다. 프로비저닝프로파일 만들때 단말목록에서 설치하고자 하는 단말을 선택할 수 있는것을 알수 있다.


이 과정은

1. 애플인증서 생성

2. 프로비저닝 생성

3. 빌드 및 실행

이 되는데 하나하나씩 정리해 보자


1. 애플인증서 생성


개발자 PC에 키체인앱은 개인키와 공개키를 가지고 있다 이를 기반으로 먼저 CSR(Certificate Signing Request)을 먼저 만들어야 한다.

(1) 키체인 앱 실행

(2) 키체인접근 > 인증서 지원 > 인증기관에서 인증서 요청 선택

(3) 개발자의 이메일주소와 이름을 입력

(4) 요청항목의 "디스크에 저장됨" 선택

(5) 계속을 누르면 CertificateSigningRequest.certSigningRequest 파일이 저장된다.


저장된 CSR파일을 애플개발자 콘솔에 인증서 추가 화면에서 등록한다.

(1) https://developer.apple.com/ 접속 및 로그인

(2) Certificates, Identifiers and Profile > Certificates > Development 이동

(3) 추가버튼 누르고 iOS App Development선택하고 화면 안내에 따라 CSR파일 등록 

(4) 등록 완료되면 인증서 파일 다운로드


동일한 방법으로 Production의 AppStore/Enterprise 와 Ad Hoc 선택하여 배포용도 등록한다. 


2. 프로비저닝 생성


프로비저닝은 앱아이디별로 생성이 가능하며 앱아이디를 먼저 등록해주어야 한다.

등록된 앱아이디로 Provision Profiles 화면에서 안내에 따라 개발과 배포용을 등록한 후 프로비저닝 파일을 다운로드 한다.

이 때 개발용의 경우 설치를 허용할 단말을 선택할 수 있는데 이 화면에서 선택한 단말들만 해당 프로비저닝으로 설치및 실행이 가능하다.


위 과정을 거치면 


1. 개발자의 공개키와 개인키

2. 애플이 인증한 인증서

3. 디바이스에 설치 가능한 프로비저닝


위 3가지로 빌드를 하면 .app파일이 생성되며 생성된 app파일은 다음 2가지로 구성된다.


1. 실제로 사용될 프로비저닝 프로파일 : 빌드시 사용된 프로비저닝이다.

2. _CodeSignature 폴더 : CodeResources 파일을 담고 있고 모든 파일의 암호화 해시정보를 갖고 있다.


이렇게 만들어진 .app파일이 설치되어 실행될떄 Adhoc, Enterprise, AppStore가 각각 다르게 실행이 되어 진다.


1. AdHoc버전의 앱은 디바이스에 설치되어 실행될때 


(1) .app에 포함된 프로비저닝 프로파일이 애플인증서로 서명되었는지 확인

(2) CodeResources참조하여 모든 파일의 해시를 확인하여 무결성 체크

(3) 디바이스에 프로비저닝 프로파일이 있는지 확인


2. 엔터프라이즈배포의 경우는 애플은 그 회사를 신뢰하고 별도의 확인과정을 거치지 않고 바로 실행되게 해준다.


3. 앱스토어배포의 경우는 그 어떤 디바이스에서도 실행될수 없다. 오로지 애플에 제출용으로만 사용이 가능하며 애플이 리뷰후 다시 사인하여 배포한다.


만일 새로운 팀원이나 다른 PC에 개발환경을 설정할 때 매번 애플인증서를 만들수는 없다.


이 때는 이미 등록된 인증서와 해당 인증서를 등록한 개발자의 키파일을 전달 받아서 설치해주어야 한다. 그러지 않으면 인증서의 출처를 확인하지 못하여 정상적으로 빌드가 안된다.



#ios #xcode #Code_signature

OOP 설계 사례

 객체지향이라는 종교에 가까운 프로그래밍 방법론에 대해서는 이게 정답이냐 하는 논쟁거리는 뒤로 하고(https://samse.tistory.com/entry/Goodbye-OOP 참고) 일반적으로 어떤 기능 또는 자연계의 현상을 컴퓨터가 이해할 수 있...