SwiftObjcCppDuplex
This is a tiny sample project to show the two-way communication between one Swift object and another in C++ via Objective-C.
Background and Motivation
The bridging header makes it possible for Swift objects to access Obj-C objects. And [Project]-Swift.h precompiled header makes it possible for Obj-C objects to access Swift objects, whose classes and functions are makred by @objc
. The interoperability between Obj-C and C++ are well defined, though it is a bit cumbersome, if you want to completely isolate the C++ code from the Obj-C++.
A difficulty arises when you want to achieve the two-way communication between a Swift object and an Obj-C object. Apparently it is not easy to make the direct duplex communication between those two, in the sense that both sides have a reference to each other and have access to each other’s member functions and variables. It is by no means straightforward, but after some research and experiments, I have found a way to enable duplex communication. It involves 4 shim classes, two of which are singletons, and it is not pretty, but I could not find any better way to do it, and I though it was worth sharing.
Overview
-
SwiftEnd.swift
: The Swift class that communicates withCppEnd
. It instantiates and ownsShimObjc
. -
CppEnd.{hpp,cpp}
: The C++ class that commnicates withSwiftEnd
. It instantiates and ownsReverseShimCpp
. -
ShimObjc.{h,mm}
: The hhim in Obj-C class in the direction fromSwiftEnd
toCppEnd
. It instantiates and ownsCppEnd
. -
ReverseShimCpp.{h,mm}
: The shim in C++ class whose implementation uses Obj-C in the direction fromCppEnd
toReverseShimObjc
. It refers to the singletonReverseShimObjc
. -
ReverseShimObjc.{h,mm}
: The shim in Obj-C in the direction fromReverseShimCpp
toReverseShimSwift
. It is implemented as a singleton, which is instantiated at the first call togetInstance()
to avoid the race condition withReverseShimSwift
, which is also a singlton. This is implemented as a singleton so that the other Obj-C objects can discover it and can accessSwiftEnd
, but it can be a normal object instantiated and owned byReverseShimCpp
, if such an access is not needed. It refers to the singletonReverseShimSwift
. -
ReverseShimSwift.swift
: The shim in Swift in the direction fromReverseShimObjc
toSwiftEnd
. It is implemented as a singleton, which is instantiated in the initialization block of the object file. As it is a singleton, the object can be discovered by a class functiongetInstance()
by bothSwiftEnd
andReverseShimObjc
.SwiftEnd
callsgetInstance()
so thatReverseShimSwift
can discover the correspondingSwiftEnd
object.
Call Sequence
The SwiftEnd
object is instantiated in App
and it is pushed to SwiftUI as an environment object.
It is picked up in ContentView
. The button in the view calls SwiftEnd
‘s callCpp()
function, which eventually reaches to CppEnd::callBackToSwift()
, which in turn calls ReverseShimCpp::callBackToSwift()
, which eventually reaches again to SwiftEnd.callBackToSwift(), which reads the passed parameter and returns another value, which is returned all the way back to the original call to SwiftEnd.callCpp()
.
The call chain above shows a two-way communication between a Swift object (SwiftEnd) and a C++ object (CppEnd).