How to Make Objective-C Class Using Std:Vector Made Available to Swift Classes

How to make Objective-c class using std:vector made available to Swift classes

Swift only supports bridging to Objective-C. You need to move any CPP code / declarations to the .mm file, such as:


Foo.h

#import <Foundation/Foundation.h>

@interface Foo : NSObject

- (void)bar;

@end

Foo.mm

#import "Foo.h"
#import <vector>

@interface Foo() {
std::vector<int> _array;
}

@end

@implementation Foo

- (void)bar {
NSLog(@"in bar");
}

@end

One solution, if you have to consume the C++ classes in other C++ / Objective-C++ code, to create a separate header file for Swift's bridging header, and expose what you need:

Foo.h

#import <Foundation/Foundation.h>
#import <vector>

@interface Foo : NSObject {
std::vector<int>* _bar;
}

@property (atomic, readonly) std::vector<int>* bar;
@property (readonly) size_t size;

- (void)pushInt:(int)val;
- (int)popInt;

@end

Foo+Swift.h

Include this in your bridging header

#import <Foundation/Foundation.h>
#import <stdint.h>

@interface Foo : NSObject

@property (readonly) size_t size;

- (void)pushInt:(int)val;
- (int)popInt;

@end

Foo.mm

#import "Foo.h"

@implementation Foo

@synthesize bar;

- (instancetype)init {
if (self = [super init]) {
_bar = new std::vector<int>();
}

return self;
}

- (void)dealloc {
delete _bar;
}

- (void)pushInt:(int)val {
_bar->push_back(val);
}

- (int)popInt {
if (_bar->size() == 0) {
return -1;
}

auto front = _bar->back();
_bar->pop_back();
return front;
}

- (size_t)size {
return _bar->size();
}

@end

main.swift

#import Foundation

let f = Foo()
f.pushInt(5);
f.pushInt(10);

print("size = \(f.size)")
print("\(f.popInt())")
print("\(f.popInt())")
print("size = \(f.size)")

Most efficient way of getting large std::vector into Swift?

Some parts are not clear enough but I will try to show you some example.

First of all, you need to prepare a class which can access your contourVector. (I cannot see if it is an instance field or a global variable, if it is an instance field, you may use the existing class.)


Create a header for the prepared class, again you may utilize the existing header, but this header needs to be compiled both in C-context and in C++ context. So, if your existing header contains some declaration which cannot be compiled in C-context, you may need separated two headers or some #ifs.

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface YourClass : NSObject

- (NSInteger)contoursSize;
- (NSInteger)contourSizeAtIndex:(NSInteger)index;
- (CGPoint *)contourAtIndex:(NSInteger)index;

//...

@end

NS_ASSUME_NONNULL_END

Then add 3 methods to the class specified in the header:

#import "YourClass.h"
#import <vector>

typedef std::vector<CGPoint> CGContour;
typedef std::vector<CGContour> CGContours;

static CGContours contourVector;

@implementation YourClass

- (NSInteger)contoursSize {
return contourVector.size();
}
- (NSInteger)contourSizeAtIndex:(NSInteger)index {
return contourVector[index].size();
}
- (CGPoint *)contourAtIndex:(NSInteger)index {
return contourVector[index].data();
}

@end

Please do not forget to include the header inside your Project-Bridging-Header.h:

//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "YourClass.h"

You need to create a Swift side wrapper class, as you cannot create UnsafeBufferPointer in Objective-C.

class YourClassWrapper {
let yourInstance = YourClass()

var count: Int {
return yourInstance.contoursSize()
}

subscript(index: Int) -> UnsafeBufferPointer<CGPoint> {
guard 0..<count ~= index else {fatalError("Index \(index) out of bounds \(0..<count)")}
let start = yourInstance.contour(at: index)
let count = yourInstance.contourSize(at: index)
return UnsafeBufferPointer(start: start, count: count)
}
}

With these preparations above, you can access each CGPoint as:

let wrapper = YourClassWrapper()
let point = wrapper[0][1]

Or you can get the pointer to the first element in CGContour as:

let ptr = wrapper[0].baseAddress!

You may need to modify some parts to fit this into your actual code. Hope you can make it.

Convert a vectordlib::point to an NSArray so it can be passed to a Swift class

NSArrays can only contain Objective-C objects, no C++ objects. So you have to convert your points to NSValues first. Something like this should work:

NSMutableArray* facialLandmarksArray = [NSMutableArray arrayWithCapacity: facialLandmarks.size()];

for (auto const& pt: facialLandmarks) {
[facialLandmarksArray addObject: [NSValue valueWithCGPoint:CGPointMake(pt.x, pt.y)]];
}

And make sure this is compiled as Objective-C++ (file ending *.mm).

However, note that this does not scale well for large vectors. Swift and C++ are both able to make sure that the values of such arrays/vectors are stored in consecutive memory, bridging through Objective-C destroys this locality.

Can I mix Swift with C++? Like the Objective-C .mm files

No. When you switch from .m to .mm you are actually switching from Objective-C to a different language (which has many subtle differences) called Objective-C++. So you're not really using C++; you're using Objective-C++ which accepts most C++ as input (in the same way that C++ accepts most but not all C as input). When I say it's not quite C++, consider a C++ file that includes a variable named nil (which is legal C++) and then try to compile that as Objective-C++.

Swift doesn't have the same relationship. It is not a superset of C or C++, and you can't directly use either in a .swift file.

"Using Swift with Cocoa and Objective-C" also tells us:

You cannot import C++ code directly into Swift. Instead, create an Objective-C or C wrapper for C++ code.

Objective-Zip method not available to Swift class

Eventually figured it out on my own. The problem was that the method returns an object from another file that I hadn't imported (FileInZipInfo.h), and for some reason the compiler decided that the best way to deal with that was to pretend that the method didn't exist. Once I imported the other file, everything worked fine.

Can I have Swift, Objective-C, C and C++ files in the same Xcode project?

YES.

You can mix Swift, C, C++, Objective-C & Objective-C++ files in the same Xcode project.

C

// Declaration: C.h
#ifndef C_h
#define C_h
#ifdef __cplusplus
extern "C" {
#endif
void hello_c(const char * name);
#ifdef __cplusplus
}
#endif
#endif /* C_h */

// Definition: C.c
#include "C.h"
#include <stdio.h>
void hello_c(const char * name) {
printf("Hello %s in C\n", name);
}

C++

// Declaration: CPP.hpp
#pragma once
#include <string>
class CPP {
public:
void hello_cpp(const std::string& name);
};

// Definition: CPP.cpp
#include "CPP.hpp"
#include <iostream>
using namespace std;
void CPP::hello_cpp(const std::string& name) {
cout << "Hello " << name << " in C++" << endl;
}

Objective-C wrapper for C++

// Declaration: CPP-Wrapper.h
#import <Foundation/Foundation.h>
@interface CPP_Wrapper : NSObject
- (void)hello_cpp_wrapped:(NSString *)name;
@end

// Definition: CPP-Wrapper.mm
#import "CPP-Wrapper.h"
#include "CPP.hpp"
@implementation CPP_Wrapper
- (void)hello_cpp_wrapped:(NSString *)name {
CPP cpp;
cpp.hello_cpp([name cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end

Objective-C

// Declaration: Objective-C.h
#import <Foundation/Foundation.h>
@interface Objective_C : NSObject
- (void)hello_objectiveC:(NSString *)name;
@end

// Definition: Objective-C.m
#import "Objective-C.h"
@implementation Objective_C
- (void)hello_objectiveC:(NSString*)name {
printf("Hello %s in Objective-C\n", [name cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end

Objective-C++

// Declaration: Objective-CPP.h
#import <Foundation/Foundation.h>
@interface Objective_CPP : NSObject
- (void)hello_objectiveCpp:(NSString *)name;
@end

// Definition: Objective-CPP.mm
#include <iostream>
#import "Objective-CPP.h"
using namespace std;
@implementation Objective_CPP
- (void)hello_objectiveCpp:(NSString *)name {
cout << "Hello " << [name cStringUsingEncoding:NSUTF8StringEncoding] << " in Objective-C++\n";
}
@end

Swift

// Declaration & definition: Swift.swift
func hello_swift(_ name: String) {
print("Hello \(name) in Swift")
}

Bridging-Header.h

Cannot import CPP.hpp header file, not because of it's naming convention, but because it contains the class keyword.

#import "C.h"
#import "CPP-Wrapper.h"
#import "Objective-C.h"
#import "Objective-CPP.h"

Invocation from Swift

// Invoke C
hello_c("World".cStringUsingEncoding(NSUTF8StringEncoding))

// Can't Invoke C++ without a wrapper
// CPP().hello_cpp("World".cStringUsingEncoding(NSUTF8StringEncoding))
// Invoke C++ through Objective-C
CPP_Wrapper().hello_cpp_wrapped("World")

// Invoke Objective-C
Objective_C().hello_objectiveC("World")

// Invoke Objective-C++
Objective_CPP().hello_objectiveCpp("World")

// Invoke Swift
Swift().hello_swift("World")

.h (Headers)

(See item 3 in this Stack Overflow answer)

.h: this is the tricky part, since they are ambiguously used for all flavors of C, ++ or not, Objective or not. When a .h does not contain a single C++ keyword, like class, it can be added to the ...Bridging-Header.h, and will expose whatever function the corresponding .c or .cpp functionalities it declares. Otherwise, that header must be wrapped in either a pure C or Objective-C API.

Output

Hello World in C
Hello World in C++
Hello World in Objective-C
Hello World in Objective-C++
Hello World in Swift

Comments

Cy-4AH:

Yes. You only need wrap C++ into C or Objective-C to use in Swift.

Tommy

Indeed, I have a project that does exactly that. C++ for the thrust of the abstract cross-platform model stuff with some C parts underneath; Objective-C to wrap the C++ classes for Swift purposes, Swift to bind all that to a subclass of NSDocument, with some custom views that interrogate the C stuff.

MaddTheSane

Added the extern "C" wrapper as per your excellent suggestion. To invoke the C method void hello_c(const char * name) from C++ method hello_cpp(const std::string& name), add #include "C.h" and call hello_c(name.c_str());.

Keith Adler

The new SO-32541268: Now with parameters!


► Find this solution on GitHub and additional details on Swift Recipes.

Import headers from c++ library in swift

You don't import anything in your Swift code when Objective-C headers are imported in the bridging header.
All public interfaces available from the imported files get available in the entire Swift module by default after that.


Sample listing

TDWObject.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface TDWObject : NSObject

- (void)someCPPCode;

@end

NS_ASSUME_NONNULL_END

TDWObject.mm

#include <iostream>

#import "TDWObject.h"

@implementation TDWObject

- (void)someCPPCode {
std::cout << "Hello from CPP cout" << std::endl;
}

@end

Some-Bridging-Header.h

#import "TDWObject.h"

main.swift

TDWObject().someCPPCode()

Provided the main.swift file is the entry point of the program, it will print Hello from CPP cout.



Related Topics



Leave a reply



Submit