XCode 6버전에 들어서면서 손쉽게 만들기 어려웠던 Cocoa Touch Framework 프로젝트를 만들 수 있게 되었습니다. 이 프로젝트의 경우 Target이 Framework가 되며 실제로 빌드를 할 경우 XCode DerivedData 디렉토리안에 빌드된 Framework가 생성됩니다. 이렇게 빌드된 Framework를 당신이 원하는 프로젝트에 드래그&드롭을 하게 되면 간단하게 Framework가 추가되며 안에 있는 기능들을 호출할 수 있게 됩니다.
Framework 만들기
지금부터 간단한 Framework를 제작하고 예제앱에 추가하여 사용하는 방법을 구현해 보도록 하겠습니다. 지금부터 진행할 예제는 하나의 Workspace안에 두개의 프로젝트(Framework, App)로 구현할 것인데 Workspace 없이 별개의 단일 프로젝트를 각각 구현하셔도 괜찮습니다.
File > New > Workspace를 선택하여 개발을 할 워크스페이스를 하나 만들어줍니다. 저는 HelloFrameworkExample로 만들어보겠습니다.
그럼 완전 아무것도 없는 빈 IDE가 등장합니다. 다시 File > New > Project 메뉴를 선택하여 프로젝트를 추가해줍니다.
스크린샷과 같이 Cocoa Touch Framework를 선택하고 Next를 눌러 진행합니다. 다음은 프로젝트의 기본 설정을 하게 되는데 적당히 본인의 환경에 맞춰주시면 됩니다. 언어의 경우 Swift, Objective-C 둘중에 하나를 선택할 수 있는데 저는 일단 Objective-C로 진행을 하도록 하겠습니다.
마지막에 소스가 저장될 디렉토리를 설정하는데 하단에 Add to에 현재 추가할 Workspace를 선택해주어야 작업할 워크스페이스에 추가됩니다. Git Source Control은 예제 프로젝트이므로 사용하지 않을 생각입니다.
생성된 Cocoa Touch Framework의 디렉토리 구조도는 위와 같습니다. Framework Import시에 가장 기본으로 불려질 HelloFramework.h와 프로젝트 설정이 담겨있는 Info.plist와 테스트 코드들이 담겨있습니다.
소스파일이 아닌 프로젝트 자체를 선택했을때 오른쪽에 뜨는 프로젝트 설정에 주목할 부분은 Deployment Target 설정입니다. Framework가 갖는 특성이 다양한 환경에서 실행되어야 하는 라이브러리라고 볼 때 더 많은 환경에서 구동되도록 구현하는것은 당연해 보입니다. Deployment Target의 경우 실행 가능한 가장 낮은 버전을 설정하게 되는데요. 현재 시점에서 선택할 수 있는 합리적인 버전은 5.1.1이지 않나 생각합니다.
2015년 2월부터 Apple에서는 앱스토어에 등록하는 모든 앱이 64비트를 지원할것을 강제화 했습니다. Framework가 32비트와 64비트를 동시에 지원할 수 있는 가장낮은 Deployment Target은 5.1.1입니다. 실제로 더 낮은 버전을 설정하면 다음과 같은 경고 메시지를 볼 수 있습니다.
위의 Deployment Target 설정과 연계하여 Build Setting에 대해서도 확인해 봅시다.
여기서 주목해야 하는 설정은 Architectures와 Valid Architectures 설정입니다. 보면 32비트 아키텍쳐인 armv7/armv7s와 64비트 아키텍쳐인 arm64를 모두 지원하는것을 알 수 있습니다. 그 다음으로 확인해야 하는 부분은 Build Active Architecture Only 설정입니다. 기본 설정은 Debug에 한해 Yes로 되어있습니다만 이렇게 되어있을 경우 Simulator 환경에서는 i386으로만 빌드가 됩니다. 하지만 당신의 환경이 x86_64 아키텍쳐 환경을 필요로 할 수 있으므로 No로 바꾸어 가능한 모든 환경에서 동작하도록 빌드를 합시다.
이제 간단한 예제 API를 만들어봅시다. File > New > File 메뉴를 선택하거나 마우스 우클릭 후 Add File을 선택하여 Cocoa Touch Class를 추가합니다.
Person이라는 클래스를 추가해보겠습니다. IDE에 새로운 두개의 파일(.h/.m)이 생성된것을 확인할 수 있습니다. 다음과 같이 간단한 데모 코딩을 해보겠습니다.
#import <Foundation/Foundation.h> @interface Person : NSObject - (instancetype)initWithName:(NSString *)name age:(int)age; - (void)info; @end
#import "Person.h" @interface Person () @property(strong, nonatomic) NSString *name; @property(nonatomic) int age; @end @implementation Person - (instancetype)initWithName:(NSString *)name age:(int)age { if(self = [super init]) { self.name = name; self.age = age; } return self; } - (void)info { NSLog(@"The person's name is %@ and his age is %d", self.name, self.age); } @end
위의 코드에 대한 내용은 굳이 설명하지 않아도 괜찮을 것이라 생각하고 패스하겠습니다^^ 하지만 초기화후에 info를 호출하여 NSLog로 값들을 출력한다는것만 기억해두도록 합시다.
이렇게 추가된 클래스가 Framework 외부의 프로젝트에서 접근가능해야 할 경우 위와 같은 설정이 필요합니다. 클래스의 헤더파일을 선택하면 (위의 예시는 Person.h) 오른편에 Target Membership란이 보이는데 Framework와 Test가 보입니다. Framework 오른편에 Visibility 설정을 할 수 있습니다. Public으로 바꿔주어야 이 Framework를 사용하는 다른 프로젝트들이 자유롭게 이 클래스를 사용 할 수 있습니다. 기본적으로 Project로 되어있는데 Project의 경우 해당 프로젝트 이내(여기서는 Framework 자기 자신)에서만 접근이 가능하며 Private는 프로덕트에는 포함이 되지만 사용되기 원하지 않는 개발단계의 클래스들을 지정할 때 사용합니다.
마지막으로 HelloFramework.h 헤더파일에 추가한 클래스를 Import 선언해 줍니다. 여기에 선언하는 모든 클래스는 이 Framework를 Import하였다면 자유롭게 사용할 수 있게 됩니다. Framework 내부에서만 사용될 클래스는 여기에 선언하지 않도록 합니다.
#import <UIKit/UIKit.h> //! Project version number for HelloFramework. FOUNDATION_EXPORT double HelloFrameworkVersionNumber; //! Project version string for HelloFramework. FOUNDATION_EXPORT const unsigned char HelloFrameworkVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import <HelloFramework/PublicHeader.h> // 외부에서 자유롭게 접근할 클래스의 헤더들을 다음처럼 선언해 줍니다. #import <HelloFramework/Person.h>
이것으로 예제 Framework를 만들어 보았습니다. 이제 한번 빌드를 해보도록 하겠습니다. Product > Build 메뉴를 선택합니다. 특별히 잘못한 것이 없다면 오류 없이 빌드가 성공할 것입니다.
처음에 보면 Products 이하의 파일들은 빨간색으로 표시되고 있습니다. 파일이 없는(Missing) 상태임을 뜻합니다. 빌드를 하면 생길것이라 생각되어 빌드를 해도 변화가 없는것을 확인할 수 있습니다. Products 이하의 파일들은 실제 환경인 Device 환경에서 빌드된 파일들을 표시합니다. 처음에는 기본적으로 Simulator가 선택되어있어 Simulator 빌드가 수행되므로 여기에 표기가 되지 않습니다.
상단의 Build Scheme 설정 옆에 iOS Device / Simulator를 선택할 수 있는 부분이 있습니다. 여기서 iOS Device를 선택합니다. 이후에 다시 Build를 수행하면 Products 이하의 바이너리들이 검정색으로 바뀝니다. 빌드가 성공했군요.
빌드된 .framework파일을 선택하고 오른쪽의 Inspector를 보면 Full Path가 표시됩니다. 마지막에 화살표 아이콘이 있는데 저 아이콘을 누르면 해당 디렉토리로 바로 이동하여 Framework 파일의 위치를 보여줍니다. 이창을 띄워둔 상태로 예제앱을 만들어보도록 하겠습니다.
예제앱 만들기
이번에는 만들어둔 Framework를 활용한 간단한 예제앱을 만들어보도록 하겠습니다. File > New > Project를 선택하여 새로운 프로젝트를 생성합니다. 프로젝트 이름은 HelloApp으로 하겠습니다.
간단히 Framework가 동작하는것을 확인하기만 할것이기에 무엇을 선택해도 상관없지만 Single View Application으로 만들어보겠습니다.
마찬가지로 Add to 설정에서 기존에 만들어둔 Workspace를 재활용 할 수 있도록 설정해 줍니다. 이제 HelloApp도 XCode에 추가되어 작업할 수 있게 되었습니다.
아까 열어두었던 창의 HelloFramework.framework 파일을 드래그하여 추가해줍니다. 여기서 복사를 하여도 되고 Framework가 계속 변화가 이루어지는 상황이라면 Reference만 추가해놓고 Framework의 업데이트가 바로 적용되도록 할 수도 있습니다. 이제 Framework를 Import하여 사용해 보도록 하겠습니다. 첫번째 뷰인 ViewController.m에 추가해보겠습니다.
#import "ViewController.h" #import <HelloFramework/HelloFramework.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // Framework의 Person 클래스를 사용 Person *person = [[Person alloc] initWithName:@"Hello" age:19]; [person info]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
Person클래스의 info메소드를 호출하게 되면 NSLog를 통해 이름과 나이를 출력하게 만들어져있는것을 이전에 확인하였습니다. 정말로 제대로 호출이 되는지 실행을 해보겠습니다.
실행전에 Build Scheme과 적당한 실행 디바이스가 선택되어있는지 확인하고 빌드하시기 바랍니다. 저는 일단 심뮬레이터에서 구동해보겠습니다.
원하는 값이 정상적으로 출력이 되었군요^^