2

I want to change the properties of some objects (Labels, Buttons..), I created using the Storyboard out of c++-code. So I need a way of running ViewController-class-internal methods.

Is there any proper way to do this? Is there another possibility?

I've tried using callbacks, but there is always this barrier between global and internal in the ViewController-class. Thanks in advance!

EDIT: Since I don't know how to access a swift class out of c++ code, i cannot give any proper examples, but I thought of something like this (pseudo code):

In c++:

int main(){
    say_hello();
}

and in Swift:

class ViewController: NSViewController {
    @IBOutlet weak var label: NSTextField!
    func say_hello(){
        label.stringValue = "Hello"
    }
}
archimedes
  • 87
  • 1
  • 8
  • 2
    I do all my swift/c++ communication through an objective-c++ thunk. – Richard Hodges Aug 28 '16 at 10:31
  • If you could provide some brief code snippets illustrating the problems you're running into someone here might be able to help you. – Anatoli P Sep 01 '16 at 01:54
  • @OmniProg: I added some examples of what would be useful (did omit the header-files), but I don't think it will work in any similar way. Is there any possible way of archiving this: `event in cpp-class` -> `calling swift-method` -> `change label-value` (as an example) ? – archimedes Sep 02 '16 at 14:39

1 Answers1

2

Here is an oversimplified example of how this could be done using an Objective-C++ wrapper, as suggested by Richard. Memory management and thread safety aspects, and many other things, are not addressed here. In this example there is a 1-to-1 relationship between Swift and C++ class instances. Also, Swift object pointers are used as identifiers to decide which Swift object should receive a notification. This is kind of dangerous, see comments in the code below. Using more sophisticated data structures in the Objective-C++ wrapper to maintain a connection between Swift and C++ objects, one could easily work around this danger and support relationships other than 1-to-1.

First of all, here is a C++ class that triggers changes in Swift code:

typedef void (*cb_t)(const char *, void *);

class MyClassCPP {
public:
    MyClassCPP(cb_t callBack, void * p) : myCallBack(callBack), clientPtr(p) {}
    void doWork();  // perform some work and invoke the callback
private:
    cb_t myCallBack;
    void * clientPtr;
};
void MyClassCPP::doWork() {
    myCallBack("C++ code at work...", clientPtr);
}

Here is an Objective-C++ wrapper interface that should be made visible to Swift code via the bridging header, directly or indirectly. Please note that it does not reference any C++ types.

@class SwiftClass;  // forward declaration
                    // can't include *-Swift.h in a header

@interface OCWrapper : NSObject

-(instancetype)init:(SwiftClass * )sc;
-(void)requestWorkFromCPP;

@end

And here is the wrapper implementation. It does reference C++ types. We cannot provide a Swift global function as a callback to C++ code, but we can provide an Objective-C++ global function for this purpose.

// Extension that deals with C++ specifics that can't be visible to Swift
@interface OCWrapper ()
{
    MyClassCPP * myClassCPP;
}
@end

void callBack(const char * msg, void * swiftClient)
{
    // Danger: what if swiftClient does not point to a SwiftClass instance?
    [(__bridge SwiftClass*)swiftClient sayHello: 
       [[NSString alloc] initWithBytes: msg length:strlen(msg)
        encoding:NSASCIIStringEncoding]];
}

@implementation OCWrapper

-(instancetype)init:(SwiftClass * )sc
{
    myClassCPP = new MyClassCPP(callBack, (__bridge void*)sc);
    return self;
}

-(void)requestWorkFromCPP{
    myClassCPP->doWork();
}

@end

The above should be in an Objective-C++ file. Create an Objective-C file and then rename it to have the .mm extension. You will also need to include the *-Swift.h header, so Objective-C++ can use Swift types.

Finally, here is some Swift code that uses the C++ code via the Objective-C++ wrapper:

// This is like your Swift view controller
class SwiftClass : NSObject
{
    var label = "[Empty]"

    var name : String;
    init(name : String) {
        self.name = name
    }

    func sayHello(greeting : String) {
        label = "SwiftClass named " + name + " received greeting: " + greeting
    }
}
...
        let sc = SwiftClass( name : "Zero")
        let ocWrapper = OCWrapper(sc)
        let sc1 = SwiftClass( name : "One" )
        let ocWrapper1 = OCWrapper(sc1)

        ocWrapper1.requestWorkFromCPP()
        print("The label value from C++: \(sc1.label)")

        ocWrapper.requestWorkFromCPP()
        print("The label value from C++: \(sc.label)")
...
Anatoli P
  • 4,791
  • 1
  • 18
  • 22