Day 8 : 15 June 2022 : iOS Part 3 - Integrate Ava using Objective-C

My 100 Daze of Code

https://github.com/davidjwalling/100-days-of-code

#8 : iOS Part 3 Integrate Ava using Objective-C

Lots of changes today. We'll complete our first look at iOS by integrating our Project Ava code into our sample iOS app. We'll also relocate some files in the repository to improve organization. I've included some regression testing at the end to confirm that our changes are still working on macOS, Linux and Windows too.

Updates to the Repository Structure

I've put all of our Windows-related files (.sln, .props, .vcxproj, etc.) and iOS-related files into "windows" and "ios" folders, respectively, and have adjusted the solution and project to refer to these files in their new locations. This will help isolate files that are platform-specific. The same source files at the repository base are referred to by both Windows and iOS project files. Also, I've renamed the "build" folder to "cmake" because that is more descriptive of the files contained in that folder.
For now, source code is still at the base of the repository. And, we will continue for now with a single CMakeLists.txt file. As we add additional artifacts, we will reconsider this approach.

Updates to the Ava Library

api.h

Since we use the __stdcall convention for 32-bit Windows targets, we need to define our main routine as __cdecl. For non-Windows platforms, this is defined away to avoid a compilation error.

driver.h

The IDriver class now includes an alternate constructor that sets the message that will be returned. We'll call this the "title". Also, the former "Hello" routine that sent our hello message to the standard output stream will now return this message as a std::string. The application layer will report this message as appropriate.

driver.cpp

Note that the initializer for _impl, called in the default and alternate constructors is invoked before the body, so that the deference to _impl->Title is not called until _impl exists.


Updates to the Executable

We've moved the call to std::cout to the main routine because on iOS, our app will use different code to display the message. So instead, after constructing the class instance by passing the message of the day, we'll invoke the IDriver::Title method to return the message to display.


The Objective C Wrapper

To invoke our IDriver methods from within our SwiftUI app, we wrap the C++ class methods in an Objective C wrapper. Note that we've recreated the ava project in Xcode. It is how a project created in the "ios" folder, not part of a workspace. We've used the File|Add File To ... menu item to reference our library source files (api.h, driver.h, driver.cpp) even though they are at a higher level in the folder hierarchy.

wrap_driver.h

The Objective C wrapper header defines the interface into wrap driver with an instance type that wraps the IDriver constructor and a Title method that wraps IDriver::Title.

wrap_driver.mm

The Object C wrapper source (wrap_driver.mm) implements the wrapper methods. The initializer creates and invokes the constructor for IDriver passing a title - the message of the day. The Title method invokes the IDriver instance's Title method to return the message.

The Bridging Header - ava-Bridging-Header.h

To bridge from Swift to Objective C, Xcode will prompt and create a "bridging header". For now, we only need to import the wrapper header here.


Updates to the SwiftUI App

We've updated ContentView.swift to invoke the wrap_driver initializer, passing our message of the day, "Hello, Day 8!". Then, we conditionally assign a variable, text1, to the value returned by the title method, if the initialization succeeded. Then we call Text to output our message with either the value of text1, if that has been set, or with a default message.


Updates to the iOS App Project

The last time we built our iOS app, we saw a build error related to Metal API validation. Since we don't use Metal, we'll disable that to avoid the error. Use the Product|Scheme|Edit Scheme... menu option to open the Scheme dialog. Under "Run" and "Diagnostics", clear the "Metal" "API Validation" check box.



Debugging Ava on iOS

Build the app on macOS (command-B) and run the debugger. I set a breakpoint at the creating of the wrapper object. We can step-over and then run to see the message of the day displayed on the iPad.



Observe the correct value assigned to the text1 variable after calling the title() method.


Regression Test to Validate on macOS, Linux and Windows

macOS (CMake)

macOS (Visual Code)

Linux (CMake)

Linux (Visual Code)


Windows (CMake)

Windows (Visual Studio)


Looking Forward

We've reached a major milestone here, now that we have our initial four operating systems building and debugging our library and app. We'll next add some network code to listen for and accept a TCP connection, receive a message and return a reply. Also, we'll start toward our next major objective - having Ava run as a service on Windows and a daemon on Linux and macOS.





Comments

Popular posts from this blog

Day 12 : 19 June 2022 : Adding Windows Service Logic

Day 5 : 12 June 2022 : CMake on Windows

Day 11: 18 June 2022 : Handling Program Arguments