Port an iOS app to macOS 10.14 in 5 minutes
Here’s how to port your iOS apps to macOS 10.14 Beta using Apple’s iOSMac/Marzipan framework. A “Hello World” iOS app can become a macOS app in less than 5 minutes. (I timed it.).
This post will show you how to use my tools to build your own iOSMac apps on macOS 10.14 Mojave beta.
Edit June 7: @NSBiscuit released an easier method for making iOSMac apps. Try that method if this tutorial doesn’t work.
Introduction
Apple announced at WWDC that they were developing a framework, named iOSMac/Marzipan, to allow iOS developers to build apps for macOS, and revealed that the Home, Stocks, News, and Voice Recorder apps in macOS 10.14 are all ported from iOS with this framework.
While Apple did not provide official support for converting iOS apps to macOS apps, it’s simple to disable the check in macOS and preview the future of unified iOS and macOS development.
Obviously, you need the source code of the application to port; Most applications will not run without modification, as many frameworks and classes are missing, preventing most third party libraries such as Facebook SDK and HockeyApp from running.
For example, I attempted to port the Kickstarter app, which failed due to its use of the Facebook SDK, HockeyApp, and Stripe.
However, both Swift and Objective-C are supported, and Storyboards/Interface Builder layouts are mostly working, so many simple apps should work.
Video tutorial
I made a five minute video tutorial showing how to install MarzipanTool, how to enable iOSMac for third-party programs, and how to configure an Xcode project to build with iOSMac.
For more details, here’s a text-based tutorial.
Initial Setup - Only has to be done once
You will need a Mac running macOS 10.14 Beta, with Xcode 10 Beta installed. (It must be a physical Mac: virtual machines cannot show iOSMac applications, which require GPU acceleration.).
Disable SIP
You need to disable System Integrity Protection on your computer. You can find tutorials online, such as this one.
Note that disabling System Integrity Protection makes your Mac insecure, but is needed for enabling iOSMac support. Please reenable SIP when you’re not running iOSMac applications.
Select Xcode
Make sure Xcode Beta is selected as the active Xcode.
sudo xcode-select --switch /Applications/Xcode-beta.app
Download and Install MarzipanTool
To enable third party applications to use the iOSMac framework, I’ve developed a set of tools named MarzipanTool.
First, install MarzipanTool’s Swift libraries and the linker wrapper on your computer. Run
git clone https://github.com/zhuowei/MarzipanTool.git
cd MarzipanTool
./installtool.sh
Note that this assumes /usr/local/
is writable. If you have Homebrew installed, it should be writable. If not, run sudo ./installtool.sh
instead.
Enabling iOSMac
These steps bypasses macOS’s restrictions and allows your own iOSMac applications to run. You need to keep these programs running when you are testing your own iOSMac applications.
First, open Voice Memos to make sure iOS services are started.
After it starts, in one terminal, run
cd MarzipanTool
./uikitsystemenabler.sh
You should see something like this:
computer:MarzipanTool zhuowei$ ./uikitsystemenabler.sh
(lldb) process attach --name "UIKitSystem"
Process 604 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00007fff67fb50d6 libsystem_kernel.dylib`mach_msg_trap + 10
libsystem_kernel.dylib`mach_msg_trap:
-> 0x7fff67fb50d6 <+10>: retq
0x7fff67fb50d7 <+11>: nop
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x7fff67fb50d8 <+0>: movq %rcx, %r10
0x7fff67fb50db <+3>: movl $0x1000020, %eax ; imm = 0x1000020
Target 0: (UIKitSystem) stopped.
Executable module set to "/System/Library/CoreServices/UIKitSystem.app/Contents/MacOS/UIKitSystem".
Architecture set to: x86_64h-apple-iosmac.
(lldb) command source -s 0 'uikitsystem.lldb'
Executing commands in '/Users/zhuowei/Documents/MarzipanTool/uikitsystem.lldb'.
(lldb) breakpoint set -F "-[FBProcess hasEntitlement:]"
Breakpoint 1: where = FrontBoard`-[FBProcess hasEntitlement:], address = 0x000000010017d23f
(lldb) breakpoint command add
Enter your debugger command(s). Type 'DONE' to end.
(lldb) continue
Open another terminal and run
cd MarzipanTool
sudo ./uikitenabler.sh
You should see something like
computer:MarzipanTool zhuowei$ sudo ./uikitenabler.sh
dtrace: script 'tracescript.d' matched 1 probe
dtrace: allowing destructive actions
Keep these two terminal windows open.
Porting an application
Apps can be compiled for iOSMac by building a modified Simulator binary using MarzipanTool’s linker wrapper.
Unsupported features
iOSMac only supports a subset of UIKit features. Thus, most nontrivial apps will not run without modification. For example, the Kickstarter app can’t build due to these missing classes:
- UIWebView - Used by HockeyApp SDK, Facebook SDK, and some pages in the app
- SFSafariViewController - used by the app
- CTTelephonyNetworkInfo - used for analytics
- SLComposeViewController - used for email share
- MFMailComposeViewController - used for email share
- OpenGLES - used by OpenTok for rendering
- AddressBook - used by Stripe SDK
So if your app uses HockeyApp, Facebook SDK, or Stripe SDK, it will fail to build.
In addition, there are many issues, possibly due to differences between the iOS Simulator and iOSMac’s build of UIKit:
- a UILabel set to center in Storyboard instead aligns to the right (?!).
- UIScrollViews and UITableViews must have both Scroll Indicators disabled in Storyboard, otherwise the application crashes on launch
Proper iOSMac headers may fix some of these issues.
If you have any advice on shimming/replacing these classes, please update this guide!
Modifying build settings
Bundle ID: Change bundle identifier to “com.apple.stocks”. (This is required due to a limitation of my approach for enabling iOSMac. Other approaches such as Hamzasood’s bootloader patch allow using any bundle id, but are more difficult to install.)
Enable linker wrapper: Go to your project’s configuration, choose Build Settings, select All, then search for Other Linker Flags. Add the line
-fuse-ld=/usr/local/share/marzipantool/ldwrap
Fix Text Rendering: Drag the NSBundle+AppKitCompat.m
file from the MarzipanTool repo (if you’re using Objective-C) or the NSBundle+AppKitCompat.swift
(if Swift) file to your project. Otherwise Storyboard UILabels crash the application.
Build the application
- Choose iPhone 8 Plus Simulator as the build target device.
- Product -> Clean Build Folder
- Product -> Build
Run the application
- Make sure that the two enabler scripts are still running, and that lldb is not stuck.
- Open the Products group in Xcode’s left side, right click the .app, and select Show in Finder
- Right click and select Show Package Contents
- Double click the main executable to launch app.
Issues?
If you encounter any problems, please open an issue on my GitHub repository.
What I learned
- iOSMac is disappointingly limited in its compatibility with existing libraries due to its missing frameworks and classes, but surprisingly usable for simple apps.
- Dtrace is powerful if one can recognize its potential and avoid its limitations
- LLDB Scripting is clunky, buggy, and essential to automating runtime program modification
- Solving problems require trying many different approaches; it’s important to realize when to try a new method
Thanks
Thanks to @stroughtonsmith, @mistydemeo, and @KhaosT for their help on Twitter, and thanks to @hamzazood for showing iOSMac for third-party applications is possible.