Writing apps using Swift and C++

Last modified: Oct 9, 2017 10:13 AM
3 8005 Last modified Oct 9, 2017 10:13 AM

This User Tip describes a technique for writing apps (mostly) in C++. If you are looking for a pure Swift tutorial, you will have to look elsewhere and pick from 10,000 other places. But if you are interested in incorporating C++ code with Swift or Objective-C apps, this User Tip may be all there is.


What you do you need to know?

1. You need to have at least intermediate knowledge of C++. You need to know how object lifetimes work. You need to know how to use pointers to C++ objects. You need to know what happens to C++ objects you create on the stack vs. on the heap.

2. You need at least intermediate knowledge of Xcode. These techniques are fully supported by all Apple products, but they aren’t very common.


Why would you want to do this?

  1. Maybe you have a lot of existing, proven code in C++. You don’t want to reinvent the wheel.
  2. This technique will result in compartmentalized, module code. You just can’t go wrong with that.
  3. C++ is a high performance, stable, mature language. It is very popular, with a large user-base and active community. Apple doesn’t control C++ so you get some insulation from the Walled Garden. You can be adding features to your app while everyone else is re-writing their code for Swift 5.
  4. C++ is platform independent. You never know.
  5. C++ has lots of features that other languages lack.


Why would you not want to do this?

  1. There will be a lot of boilerplate code to write. The code is easy and low-risk, but quite boring.
  2. You’re not going to make any friends. Let’s face it, C++ is not sexy anymore. In the app community particularly, all the cool kids are using Swift now. C++ is not going to be an asset on your resume.
  3. You’re not going to find much help. Did I mention that no one does this?


OK. So you ran the numbers and 5 pros > 3 cons. Let’s go.


The first thing to do is realize that you’re not going to use C++. You must use Objective-C++. This User Tip is about writing apps, and that means interfacing with Apple’s app developer APIs, Xcode, and Interface Builder. Even Swift has to conform to Objective-C for these purposes. Objective-C will allow an Objective-C interface on one side for those APIs that can use it, and a C++ interface for your C++ code.


Next, realize that you are probably going to be writing a framework. Technically, you probably could do it without a framework, but that would add confusion where you don’t need it. If you use a framework, then you have guarantees that the app will be self-contained and the framework will be self-contained. If there is a problem on one side or the other, it won’t impact the other side. If you have a problem, you don’t have to disclose your use of C++. It is just a framework, like any other.


Apple used to publish documentation about Objective-C++, but they have deleted almost all of that. The underlying Clang compiler still supports Objective-C and C++. That is unlikely to change any time soon. Technically speaking, Clang is now independent from Apple. Chances are, Objective-C++ is safe. Unfortunately, it is now undocumented. Here is what you need to know on that front.



The Basics of Objective-C++

  1. Keep Objective-C and C++ objects separate. Objective-C objects cannot inherit from C++ objects and vice versa. Instead, use composition via pointers.
  2. Speaking of pointers, use them. Objective-C only supports pointers. That means that Objective-C objects cannot contain C++ objects. They can contain pointers to C++ objects. And C++ can contain pointers to Objective-C objects. You just have to manually manage pointer lifetimes using standard procedures.
  3. Be careful with exceptions. C++ exceptions are incompatible with Objective-C exceptions and Swift exceptions. You can use all different types of exceptions, just not at the same time. You must make sure that, within any particular exception try/catch block, there is absolutely no possibility that some other exception could throw out of that block.
  4. Be careful with blocks. Blocks can be used with C and, hence, can be used with C++. Be extra careful if you attempt to use blocks anywhere near C++ lambda expressions. Blocks, in general, is an advanced topic. There are lots of side effects to worry about.
  5. Consider turning off ARC. If you are using C++, you will be using C++ memory management anyway. My advice would be to turn off ARC for Objective-C code as well. ARC was great when it was first introduced. But as Apple has adopted heavy use of blocks and Swift, ARC is now a source of lots of subtle and potential memory problems. The way I see it, if we now have to really understand the subtleties of strong and weak references to objects and how they relate to asynchronous blocks, autorelease, exception handling, etc. what is the value of having that big ARC black box in there?


How to actually make Objective-C++ work with Swift?

  1. Write Objective-C++ wrappers for all objects you want to use in Swift. Try to make the interfaces as simple as possible because this is a lot of mind-numbing, boilerplate code.
  2. In addition to a standard header .h files and source .mm files (this is Objective-C++, remember?), you will need a private header file for each class that you want to export into Swift. So if you have an object you want to call Foo, in Xcode, choose File > New > File > Cocoa Class (or Cocoa Touch Class). That will create files named Foo.h and Foo.m. Immediately rename Foo.m to Foo.mm to make it Objective-C++. Next, create a new header file called FooPrivate.h.
  3. Make your Foo.h header file inherit from NSObject. You can create more complex class hierarchies but your Objective-C classes will have to mirror the C++ ones. This is going to be tricky.
  4. Keep your Foo.h header file as simple as possible. Only expose the methods and properties that you must. You have to write boilerplate for each method or properly as well as a constructor and destructor.
  5. Make sure to limit includes in your Foo.h header file. All symbols must be safe for import into Objective-C and Swift. Name clashes will be impossible to resolve. If you have #defines and/or complex enums in C++, you may need to recreate them in Objective-C.
  6. Put all of your C++ includes and definitions in the FooPrivate.h file. Create a private category for your Objective-C class. In modern Xcode, this can include instance variables, properties, and methods - pretty much anything. This is where your C++ pointers will live and where your imported C++ symbols can be used. Your FooPrivate.h file can also include private Objective-C methods that have C++ arguments.
  7. In your Objective-C++ source files, import both the Foo.h header and the FooPrivate.h category. Now you can freely use C++ inside your Objective-C++ methods.
  8. You can import the private header of other classes too and make calls from one C++ object directly to another C++ object. Be careful with this though. If you are using some pure C++ object and wrapping that in your Foo class, you can’t have any extra data or state that lives only in the Objective-C wrapper code. In this case, everything must pass through the wrapper to the C++ object pointer. If you don’t understand what this means, then you don’t want to mix Objective-C++ with Swift.
  9. In your Xcode project, make sure that private header files are never public, only project.


And that is all there is to it. In retrospect, it is no more difficult than anything else in C++. 😉


Consider writing unit tests in Swift. This will give you an opportunity to see how your Objective-C interfaces will look in Swift. Recent version of Objective-C have some new capabilities for controlling this. See this Apple Developer document, in particular the sections on “Overriding Swift Names for Objective-C Interfaces” and “Refining Objective-C Declarations”.


Consider writing a dedicated Swift header. By convention, Frameworks should have a prefix to help prevent name clashes. Your Foo class should probably be named something like “MyLibFoo” and have files like “MyLibFoo.h” and “MyLibFooPrivate.h”. Swift doesn’t need that. The MyLib prefix will work in Swift, but you will have to live with names like “MyLib.MyLibFoo” and that’s ugly. Instead, create Swift class wrappers that wrap your Objective-C classes that wrap your C++ classes, like so:

public class Foo : MyLibFoo

{

}


Now, the official Swift name for your class inside its module is MyLib.Foo.


What about more complicated cases? Suppose you are wrapping some C++ library that is already using prefixes? You can use the same name because that would be a symbol clash. Instead, in your Objective-C header, prefix the C++ class name with an underscore. Leave the file names as-is. Your MyLibFoo.h file will define a class _MyLibFoo Objective-C class that wraps (by composition) the MyLibFoo C++ class. Then, in the MyLibFoo.h header file, create another class that wraps _MyLibFoo with just Foo, like so:


MyLibFoo.h

@interface _MyLibFoo : NSObject

@end

@interface Foo : _MyLibFoo

@end



MyLibFooPrivate.h

@interface _MyLibFoo ()


@property (assign) MyLibFoo * object;


@end


Don’t forget an empty implementation in MyLibFoo.mm.


(Yes, this is a convoluted example. But this example is taken almost verbatim from a real project.)


Now, you can safely just refer to “Foo” classes throughout your Objective-C++ source and those classes will be exported into Swift as Foo as well.


Last but not least, if you are using C++, it is probably because you have some great C++ library you want to use. Watch out for include file name clashes. Maybe your MyLib framework wraps the great MyLib C++ library. That means you need a framework header called MyLib.h. But there is already a MyLib.h header file for the original C++ library. In Xcode, you can define a new umbrella header with a module map file. If you use a module map file like this:

framework module MyLib {

umbrella header "MyLibFramework.h"


export *

module * { export * }

}


Then your framework header will be “MyLibFramework.h”, a perfectly plausible and reasonable name that doesn’t conflict with the “MyLib.h” C++ header that would otherwise ruin your day and maybe your week. Then, in Swift, just do “import MyLib” and it will know what to do.

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.