Creating Objective-C and C++ packages using Swift Package Manager


Swift Package Manager now supports packages with Swift, C, C++, Objective-C and Objective-C++. It should be available in the next toolchain from trunk (latest right now is 9th May, 2016).

To demonstrate how they can be used lets look at two very simple packages.

A Package with Objective-C and Swift.

This package contains 3 modules:

  • objc: an Objective-C and C mixed library.
  • objc-exec: an Objective-C executable.
  • swift-exec: a Swift executable.

The layout of the package:

├── Package.swift
└── Sources
    ├── objc
    │   ├── include
    │   │   ├── objc.h
    │   │   └── sea.h
    │   ├── objc.m
    │   └── sea.c
    ├── objc-exec
    │   └── main.m
    └── swift-exec
        └── main.swift

Package.swift

import PackageDescription

let package = Package(
    name: "Objc",
    targets: [Target(name: "objc-exec", dependencies:["objc"]),
              Target(name: "objxec", dependencies:["objc"]),
              Target(name: "swift-exec", dependencies:["objc"])]
)

objc:

sea.h :

int one();

sea.c :

#include "include/sea.h"
int one() {
    return 1;
}

objc.h :

#import <Foundation/Foundation.h>
#include "sea.h"

@interface Foo: NSObject
- (int)foo;
@end

objc.m :

#import "include/objc.h"

@implementation Foo

- (int)foo {
    return 3 + one();
}

@end

So basically we have an Obj-C class Foo with a method foo which sums 3 and result of one() which is C function.

objc-exec:

main.m :

#import <Foundation/Foundation.h>
#import "objc.h"

int main() {
    int result = [[[Foo alloc] init] foo] + one();
    printf("Hello from Objc %d", result);
    return 0;
}

Here we use the class we defined in objc module to print the result.

swift-exec:

main.swift :

import objc
print("\(Foo().foo() + one())")

And similar executable in swift.

Compile and run:

$ swift build
Compile objc sea.c
Compile objc objc.m
Linking objc
Compile Swift Module 'swift_exec' (1 sources)
Compile objc-exec main.m
Linking objc-exec
Linking .build/debug/swift-exec
$ .build/debug/objc_exec
Hello from Objc 5
$ .build/debug/swift_exec
5

Profit!

A Package with Cpp and Swift.

This package will have 4 modules:

  • cpplib: a C++ library.
  • cwrapper: a C wrapper to the C++ library since we can’t import C++ into swift.
  • cpp-exec: a C++ executable.
  • swift-exec: a Swift executable.

Layout:

├── Package.swift
└── Sources
    ├── cpp-exec
    │   └── main.cpp
    ├── cpplib
    │   ├── cpplib.cpp
    │   └── include
    │       └── cpplib.h
    ├── cwrapper
    │   ├── cwrapper.cpp
    │   └── include
    │       └── cwrapper.h
    └── swift-exec
        └── main.swift

Package.swift:

import PackageDescription

let package = Package(
    name: "Objc",
    targets: [Target(name: "cpp-exec", dependencies:["cpplib"]),
              Target(name: "cwrapper", dependencies:["cpplib"]),
              Target(name: "swift-exec", dependencies:["cwrapper"]),
    ]
)

cpplib:

cpplib.h :

namespace cpplib {

int five();

}

cpplib.cpp :

#import "include/cpplib.h"

namespace cpplib {

int five() {
    return 5;
}

}

Just a simple function five() inside namespace cpplib.

cwrapper:

cwrapper.h :

#ifdef __cplusplus
extern "C"  {
#endif

int cwrapperfive();

#ifdef __cplusplus
}
#endif

cwrapper.cpp :

#import "include/cwrapper.h"
#import "cpplib.h"

int cwrapperfive() {
    return cpplib::five();
}

Wrap the cpp library function into a C function so it can be used by swift.

cpp-exec:

main.cpp :

#include <iostream>
#include "cpplib.h"

int main() {
    std::cout << cpplib::five();
    return 0;
}

call and print result of the function inside C++ module.

swift-exec:

main.swift :

import cwrapper
print(cwrapperfive())

Call the method from the C wrapper module.

Compile and run:

$ swift build
Compile cpplib cpplib.cpp
Linking cpplib
Compile cwrapper cwrapper.cpp
Compile cpp-exec main.cpp
Linking cwrapper
Compile Swift Module 'swift_exec' (1 sources)
Linking .build/debug/swift-exec
Linking cpp-exec
$ .build/debug/cpp_exec
5
$ .build/debug/swift_exec
5

If you have any doubts feel free to ping me: @aciidb0mb3r.