2014년 8월 11일 월요일

Objective-C 프로젝트에서 Swift 코드 사용하기

앞서 Swift 프로젝트에서 Objective-C 코드를 사용하기에 관한 글을 적으면서 '과연 스위프트 모듈을 Objective-C 프로젝트에서 쓸 일이 있을까' 라고 적긴 했었는데, 실상은 왠지 쓸 수도 있을 것 같다는 느낌이었다. 그래서 관련 내용을 글로 정리해본다.
동일한 내용의 스크린캐스트

Objective-C 프로젝트에서 Swift 코드를 사용하기는 반대의 경우보다는 좀 더 간단하다. 하지만 공식 영문 문서의 내용 자체가 좀 난해한 편이었다.

우선 예제를 보자. 아래는 사용하려는 Swift 코드이다.
class SwiftModule {
    func run() {
        println("This is SwiftModule's run() method")
    }
}
코드 내용은 설명하지 않아도 될 정도로 단순하다.

자 이제 위 코드를 사용하려는 Objective-C 코드 예제를 보자.
@interface OBjCModule : NSObject
- (void)test;
@end

@implementation OBjCModule
- (void)test {
    SwiftModule *obj = [[SwiftModule alloc] init];
    [obj run];
}
@end
앞서 스위프트 코드로 만든 SwiftModule 이라는 클래스의 오브젝트를 만들어서 run() 메소드를 실행시키는 단순한 예제이다.

여기까지 코딩하고 나서 빌드를 해 보자. 100% 오류가 발생한다. -_-;;;

@objc

스위프트에는 @objc 라는 키워드가 있다. 이 키워드는 심볼을 Objective-C 네임스페이스에 알려주는 역활을 한다. 즉, 기본적으로 Swift 모듈은 Objective-C 쪽 코드에서는 찾을 수 없게 되어있는데 이를 가능하게 해 주는 것이 바로 이 @objc 키워드이다.

앞서 코딩한 스위프트 모듈의 예제를 약간 고쳐보자.
@objc class SwiftModule {
    func run() {
        println("This is SwiftModule's run() method")
    }
}
이제 빌드를 해 보자. 또 에러가 날 것이다. -_-;;;;

헤더 추가 및 프로젝트 설정

Objective-C로 코딩을 해 왔다면 헤더파일(.h) 없이는 해당 클래스를 사용 할 수 없다는 것은 잘 알 것이다. 클래스 등의 심볼의 정의가 없으면 해당 클래스나 기타 함수들을 찾을 수가 없기 때문이다. 그렇다면 Swift 코드의 헤더를 임포트 해야 하는데... 스위프트에는 헤더가 없다.

하지만 걱정할 건 없다. 우선 Objective-C 쪽 코드 상단에 아래와 같은 식으로 헤더 파일을 임포트 시키자.
#import “ProjectName-Swift.h”
ProjectName-Swift.h 라는 파일이름은 말 그대로 프로젝트 이름 뒤에 '-Swfit.h'를 붙여서 만드는 이름이다. 예를 들어 프로젝트 이름이 MyProject 라면 MyProject-Swift.h 파일을 임포트 시켜야 된다. 만약 하이픈(빼기 -) 등의 이름으로 쓰기엔 부적합한 특수 키워드가 있다면 언더스코프(underscope, '_', 언더라인 등)으로 바꿔서 적어주면 된다.

실제 프로젝트에 이 헤더파일을 추가 할 필요는 없다. 이 파일은 Swift 코드가 빌드되면서 자동으로 생성된다. 물론 이를 위해서 Xcode상에서 프로젝트(혹은 타겟)의 설정에 약간의 수정이 필요하다.


프로젝트나 타겟의 Build Settings 에서 Packaging 을 찾는다. 이 항목에서 Defines Modules 항목을 YES로 설정한다. 그리고 Product Module Name 항목은 현재 프로젝트 이름을 넣어주자. Product Module Name 항목은 기본적으로 프로젝트 이름 뒤에 특수 아이디가 붙는 형태로 기본적으로 설정되어 있는데 이러면 제대로 못 찾기 때문에 그냥 프로젝트 이름을 또박또박 적어주면 된다.

이제 빌드를 해 보자. 상황에 따라 될 수도 있겠지만, 위 예제 대로라면 아마 빌드 오류가 날 것이다.

init이 없다?

위의 스위프트 코드 예제의 경우에는 init 이라는 생성자를 만들어 두지 않았다. 따라서 Objective-C 측의 코드에서 init을 못 찾는다는 오류가 발생한다.

그렇다면 해결법은 init을 만들어 주는 건데 귀찮다. 내 경우 아래와 같은 식으로 스위프트측 코드를 수정했다.
@objc class SwiftModule: NSObject {
    func run() {
        println("This is SwiftModule's run() method")
    }
}
뭐가 바꼈는지 찾았고 이게 무엇인지 안다면 이해는 될 것이다. 바로 NSObject를 상속받은 건데, 이건 Objective-C 의 최상위 클래스이고 그래서 기본 init도 이미 만들어져 있다. 그래서 상속받는 것 만으로 문제가 해결된다.

혹시나 이걸 상속받는게 꺼림칙하다면 init을 만들어주면 아마도 될 것이다.

여기까지의 내용으로 일단 빌드가 되어야 한다. 아마도 될 것이다. -_-;

@objc 추가 정보

앞서 이야기 한 대로, @objc의 역활은 Objective-C 측에서 Swift 코드의 심볼을 파악 할 수 있게 해 주는 용도이다. 추가로, @objc 는 별칭을 붙이는 기능이 있다.
@objc(SuperGoodClass)
public class 짱좋은클래스 {
    // ...
}
스위프트의 심볼은 영문자 외에도 유니코드 대부분의 문자를 쓸 수 있다. 하지만 이런 유니코드는 Objective-C 측에서 인식하지 못 하는 심볼이다. 그래서 @objc 커맨드를 통해 Objective-C 쪽에서 이해가 가능한 이름을 붙여줄 수 있다.

그런데 이게 실제로 잘 작동하는지는 의문이다. -_-;;; 해 본 적이 없어서...

[관련글] Swift 프로젝트에서 Objective-C 코드를 함께 사용하기
[관련글] Objective-C 제너릭(Generics)
[관련글] 스위프트(Swift) 가이드

댓글 1개 :

은아월 :

글 보고 너무 도움이 되서 댓글 남겨드려요 :) 제 블로그에 해당 내용을 정리를 좀 해놓고 싶은데... ( 나중에 잊어먹으면 또 찾아봐야될거 같아서요.... ) 혹시 이 내용을 좀 참조해도 될지 궁금해서 댓글달아요 ! 좋은 정보 정말 감사합니다 !