openCV 같은 이미지, 영상처리 라이브러리는 C++로 작성되어 있는 경우가 많습니다.
영상, 이미지처리에 AI를 사용하는 경우에는 openCV를 정말 많이 사용하죠.
iOS 앱에서 혹은 프레임워크에서 C++ 라이브러리를 사용하려면 어떻게 했어야 했는지 그리고 어떻게 바뀌었는지 알아보겠습니다.
저는 C++를 하나도 모르기 때문에 챗GPT에게 Hello world를 출력하는 메서드를 가진 클래스를 만들어달라고 요청하겠습니다
Please create a c++ class that have a function of print "Hello world"
// HelloWorld.hpp
#ifndef HELLOWORLD_H
#define HELLOWORLD_H
class HelloWorld {
public:
void printHelloWorld();
};
#endif
// HelloWorld.cpp
#include "HelloWorld.h"
#include <iostream>
void HelloWorld::printHelloWorld() {
std::cout << "Hello, world!" << std::endl;
}
이 코드를 iOS 프로젝트에 추가해주겠습니다
cpp 파일을 생성하려고 시도하면 Bridging header도 같이 만들거냐고 물어봅니다.
우리는 Swift에서 사용하는 것이 목적이기 때문에 생성을 해줍니다
//xxx-Bridging-Header.h
C++ 파일을 Swift에서 사용하기 위해 HelloWorld를 감싼 Objective-C++ Wrapper 파일을 생성하겠습니다.
Cpp 파일을 사용해야 하기 때문에 .m이 아닌 .mm이 확장자입니다. (ObjC기초문법)
// HelloWorldWrapper.hpp
#ifndef HelloWorldWrapper_h
#define HelloWorldWrapper_h
#import <Foundation/Foundation.h>
@interface HelloWorldWrapper : NSObject
- (void)printHelloWorld;
@end
#endif
// HelloWorldWrapper.mm
#import <Foundation/Foundation.h>
#import "HelloWorld.hpp"
#import "HelloWorldWrapper.h"
@interface HelloWorldWrapper ()
@end
@implementation HelloWorldWrapper
- (void)printHelloWorld {
HelloWorld obj;
obj.printHelloWorld();
}
@end
Bridging header에 HelloWorldWrapper를 임포트해서 Swift에게 존재를 알려줍니다
// xxx-Bridging-Header.h
#import "HelloWorldWrapper.h"
드디어 우리는 Swift 파일을 사용할 수 있습니다
import Foundation
struct HelloWorldCaller {
func printHelloWorld() {
let wrapper = HelloWorldWrapper()
wrapper.printHelloWorld()
}
}
WWDC23에서 애플은 Mix Swift and C++라는 제목으로 Swift에서 바로 C++를 호출할 수 있고 심지어 반대의 경우도 가능하게 되었다고 밝혔습니다.
Swift에서 C++ 파일을 호출하기 위해서는 C++ 파일이 프레임워크 안에 있어야 합니다
앱 프로젝트를 만들고 그 안에 Target을 추가하는 방식으로 Framework를 만들어줍니다
Framework를 만들때 언어는 Objective-C로 해줍니다
그 안에 C++ 파일을 만들어줍니다
Objective-C 래퍼를 사용할 때와 거의 같은 코드입니다
__cplusplus만 빼고요 (C++ 환경에서 사용하겠다는 의미입니다)
// HelloWorld.hpp
#if __cplusplus // 🔥
class HelloWorld {
public:
void printHelloWorld();
};
#endif
// HelloWorld.cpp
#include "HelloWorld.h"
#include <iostream>
void HelloWorld::printHelloWorld() {
std::cout << "Hello, world!" << std::endl;
}
프레임워크의 umbrella header에 hpp를 적고 Build Phases에 추가해줍니다
// CPPFramework.h
#import <Foundation/Foundation.h>
//! Project version number for CPPFramework.
FOUNDATION_EXPORT double CPPFrameworkVersionNumber;
//! Project version string for CPPFramework.
FOUNDATION_EXPORT const unsigned char CPPFrameworkVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <CPPFramework/PublicHeader.h>
#import <CPPFramework/HelloWorld.hpp>
이후 앱의 빌드세팅에서 C++ and Objective-C Interoperability를 C++/Objective-C++로 변경합니다
Bridging header는 필요하지 않습니다
그 이후에 앱의 ViewController.swift에서 호출해주면 끝입니다
import UIKit
import CPPFramework // 🔥
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var obj = HelloWorld() // 🔥
obj.printHelloWorld()
}
}
https://www.swift.org/documentation/cxx-interop/
https://developer.apple.com/videos/play/wwdc2023/10172/
https://developer.apple.com/documentation/swift/mixinglanguagesinanxcodeproject
https://github.com/apple/swift/blob/main/docs/CppInteroperability/GettingStartedWithC%2B%2BInterop.md
https://github.com/apple/swift/blob/main/docs/CppInteroperability/CppInteroperabilityStatus.md
https://medium.com/@canakyildz/c-and-swift-mix-from-obj-c-bridging-layers-to-interoperability-c558238c5b52
비슷하게 coreML에서 머신러닝 모델을 파이토치나 텐서플로우모델로 불러오는 기능도 생겼더라고요
기존에는 swift가 다른언어와 동떨어진 섬같은느낌이었는데 이제는 무역항이생긴느낌이 들긴하더라고요 ㅎㅎ