Macos Run Ios Simulator

  1. Ipad Emulator For Mac
  2. Macos Run Ios Simulator
  3. Macos Run Ios Simulator Full
  4. Macos Run Ios Simulator Free
  5. Ios Simulator Online

Here are the best iOS emulators for Macs and Windows computers. Macs have a lot of apps. Windows PCs have a lot of apps. But compared to the iPhone, Mac, and PC app offerings combined are slim. There are literally MILLIONS of apps and games that are designed to run on iOS. The sad thing about this is that you need an iOS device to run them. It is not possible to install the iOS Simulator on any operating system except macOS; if you want to develop an app for iOS from a Windows machine then you.

Bringing iOS Apps to macOS Using Marzipanify

IpadMarch 01 2019

At WWDC 2018 Apple gave us a ‘sneak peek’ at perhaps one of the most impactful developments on macOS since the transition to Mac OS X: UIKit apps running on the desktop. Today, I'm going to detail a special tool I built, called marzipanify, to get started with UIKit on the Mac early, and start the initial bringup of your iOS app on macOS.

💡 Don't miss the other posts in this series

  • Part one: Bringing iOS Apps to macOS Using Marzipanify

  • Part two: Making Marzipan Apps Sing

  • Part three: Deeper Integration with Marzipan

Overview

Macos

UIKit on the desktop is not like traditional macOS frameworks; it has its own /System/iOSSupport directory that houses a web of dependencies taken from the iOS Simulator — iOS variants of WebKit, MapKit, and more — and is compiled as if it were a whole other platform to macOS, called 'iOSMac'. We know iOSMac by another name, rumored since December of 2017: Marzipan.

How it works

There's another reason for the iosmac distinction: many of the frameworks underneath, née iOS Simulator, clash horrendously with the built-in macOS frameworks, thanks to a decade of divergence from OS X. Both iOS and macOS today share a UIFoundation framework to help support UIKit and AppKit and provide common code, but UIFoundation makes decisions at runtime based on which platform it's running on that affect everything from Interface Builder to text rendering. At its simplest level, this means that if you link AppKit into your iOSMac app, all manner of things will explode in your app. The iosmac linker variant for iOSMac binaries explicitly prevents loading non-iosmac binaries and libraries into your code (unless they're whitelisted).

macOS has several new components to support UIKit apps. At the top level is your UIKit app, except it doesn't run directly. Its layers are hosted in another process, UIKitHostApp.xpc, which provides the AppKit-based window chrome and bridge to Mac features like the menu bar and toolbar. Each UIKitHostApp is managed by a singular UIKitSystem app, which acts as the window/process/event manager. UIKitSystem takes the place in which SpringBoard traditionally sits on iOS, acting as the shell UI for all iOSMac apps, but bridging management to macOS' own WindowServer instead of providing a homescreen of its own.

Altogether, it appears running modern UIKit on macOS is so much more complex than the more-obvious tack Chameleon took when it rewrote UIKit for the Mac all those years ago. It's not a virtual machine by any stretch, despite evolving from the iOS Simulator, but it certainly goes to great lengths to distance itself from how Mac apps traditionally work. Perhaps that is a function of its incomplete state, or perhaps it hints at a vision for a new baseline for all Mac apps.

So. Enough preamble. How do I build my own UIKit apps on macOS?

Security & Preparation

SimulatorEmulator

iOSMac apps follow the modern pattern with Apple technologies in that they are 'entitlement-gated'. This means that unless your app is signed with private entitlements from Apple, the system will refuse to launch it. Because of this, even developers hoping to experiment with building iOSMac apps early, to get a head-start on macOS 10.15's public API, are unable to build and run their code by default on macOS.

Fortunately, macOS, unlike iOS, has a system which allows you to completely disable security and bypass everything. While that sounds terrifying, don't forget that that's just how macOS worked by default a few OS releases ago. It's only in recent years that everything has been locked down tightly.

To disable security, you first need to disable System Integrity Protection from Recovery Mode, and then you add a boot argument to bypass Apple Mobile File Integrity. Once disabled, this is how your Mac will run by default. If you wish to re-enable it, you can do so in the same way.

In Recovery Mode, use Terminal:

Now you can reboot into macOS proper.

Getting up and running

At this point, you could absolutely set up a build environment for iOSMac apps, but it's an arduous and complex process.

In some form or other, you need to:

  • Copy all the UIKit headers to the Mac version of UIKit
  • Port the UIKit tbd (linker symbols definition file) to the Mac framework
  • Set up a project in Xcode that links to the Mac version of UIKit in /System/iOSSupport/PrivateFrameworks/UIKitCore.framework
  • Create a custom linker script that passes the correct arguments to build an iosmac binary instead of a macosx binary
  • (and probably a lot more)

My recommendation is simple: do not even think about it. Why? Because I have built a much, much better and safer way…

When learning about iOSMac and its similarity with the iOS Simulator, I had a hunch one would be able to make an iOS Simulator app run as an iOSMac app. I studied the built-in iOSMac apps on Mojave intently, and, from what I knew about the Mach-O binary format, I was pretty sure that I could perform the requisite changes statically on an existing binary. Enter marzipanify.

marzipanify

marzipanify is a tool I created to statically convert an iOS app built for the iOS Simulator to macOS. It means you can continue working on and building your existing iOS app from its existing project, using the existing iOS SDK, and just run the tool against the Simulator build to create a functioning Mac app. As a bonus, marzipanify will yell at you when you're linking against a framework or library that doesn't currently exist in the iOSMac runtime. It trivializes the process so you can focus on adapting your app rather than managing a build environment.

How does marzipanify work?

marzipanify does five important things:

  • it repackages your iOS app in a format macOS will be happy with
  • it redirects all your framework links to their places in the iOSMac runtime
  • it rewrites the mach header of your binaries to change the flag that determines whether they are macosx or iosmac binaries
  • it updates your Info.plist with the appropriate keys for iosmac apps
  • it re-signs your app with the private entitlement all iosmac apps require

It performs these steps recursively on your binary and its embedded frameworks.

Everything marzipanify does can be done by hand, too. Repackaging is just a lot of moving files around and creating the correct folder structure, redirecting libraries can be done with install_name_tool, and modifying the mach header can be done with a disassembler and hex editor. Indeed, that is the process by which I gradually figured out the steps in the first place. If you look at the source code, much of marzipanify is calling out to other commandline tools to do the hard work.

Using marzipanify

The easy way, if your app and its dependencies target iOS 12.0 as the minimum OS:

The more likely way, if your app needs to target an older iOS:

If your app has everything it needs included in the iOSMac runtime in Mojave, you should now have an app you can double-click and run.

Whoa whoa whoa, back up a bit. INJECT_MARZIPAN_GLUE? Explain.

In iOS 12, Apple updated the default output of the Xcode linker to use a new load command called LC_BUILD_VERSION instead of the traditional LC_VERSION_MIN_IPHONEOS and LC_VERSION_MIN_MACOSX, to prep for an environment where a single binary can run on both iOS and macOS. All the iOSMac apps included in Mojave use this method, and it's what the system expects for all iOSMac apps. The catch is that apps targeting an OS before iOS 12 use the other load command, and it's not trivial to add a whole new load command to an existing binary.

To solve this problem in a simplistic way, marzipanify has a mode where it builds a library and injects it into your app at conversion time. This library uses interposing to trick dyld, the linker, into thinking the app is running as an iosmac binary even when it's still a macosx binary. It works! It's a hack! But clearly, there be dragons.

Tune-up

Depending on how complex your app is, at this point you could be looking at a CrashReporter window and scratching your head. The thing is, not every iOS framework exists in iOSMac yet. In fact, other than WebKit, MapKit and AVFoundation, you don't have a lot to work with in this first iteration of iOSMac. Even if all the frameworks neccessary for your app are there, you might be using a number of classes that are omitted or deprecated, like UIFeedbackGenerator, UIWebView, or MFComposeViewController. At this stage, your only option is to start #ifdefing out portions of your codebase.

With enough of your app's non-essential features disabled, with any luck you should be greeted by something onscreen, and with a little cleanup you may just have a working app. Louis D'hauwe's Pixure (sadly no longer available) is one such success. James Thomson was also able to port the iOS version of PCalc to macOS with marzipanify, much like Peter Steinberger did for PSPDFKit's viewer app.

Advanced Functionality

One very interesting benefit of how 'INJECT_MARZIPAN_GLUE' works in marzipanify is that you can customize the source code you want to inject into the target app. How is this useful? Well, it allows you to patch apps that you may not have the source code to. In fact, I used this mechanism to 'port' Apple's own Contacts app to macOS. While this probably isn't something you need to know about in your own work, personally I like to keep a separation between the crazy hacks and patches I might need for bringup of a particular app, and its clean, shipping iOS project.

Make it sing

When Apple announced UIKit on the Mac, they mentioned a list of features to make a UIKit app feel more at home on the desktop, with menus and toolbars, mouse events and more. However, you may have realized that these classes are not included in the iOS SDK, and thus can't be used in your iOS Simulator build. I will detail how you can use the new Mac-specific classes to make your app look much more like a Mac app in a future post.

Conclusion

For now, I hope I've given you a superficial overview of what marzipanify is and what it can do: it's a very simple tool to kickstart your UIKit app bringup on macOS months before the SDK becomes public. It's certainly not all you need to make a great UIKit Mac app, but it's enough to get you started and thinking about the changes and redesigns you might need to make to your app and its layout to prepare it for a future on the Mac.

There's a bright future for UIKit on the Mac, and I genuinely believe that the quick & dirty ports that we've seen so far (even from Apple) are only scratching the surface of what's possible. But for now, developers gotta develop, so onwards with your quick & dirty ports and start experimenting. As always, you can find me at @stroughtonsmith.

Ipad Emulator For Mac

Besides the method in my last blog, I keep trying other methods to run the decrypted iOS App. Then I thought of the Xcode Simulator, which had no possibility to run the real iOS Apps before, due to the x86_64 platform restriction. But now, the Simulator from M1 Mac is also the arm64 architecture. Is it possible to run the decrypted iOS App in the simulator now ?

Of course, Yes Now !!!

I wrote a tool to patch a macho file from iOS platform to Simulator platform.

  • Patch all the machos (include frameworks, dylibs) within the iOS App by my tool

  • ad-hoc code signing (free developer)

    codesign -f -s - /path/to/macho

  • Drag the iOS App to iOS Simulator, click to launch

Next I will talk about how to find the patch points.

Drag the decrypted iOS App into the iOS Simulator, and click to launch.

I got the crash :

Note the Termination Reason: Binary with wrong platform.

Question: How does the OS distinguish the arm64 machos from different platforms ?

I found the answer from the dyld source code

We can see there are at least 2 kinds of load commands that can be used to mark platform:

  • LC_BUILD_VERSION

  • LC_VERSION_MIN_XXX

Macos Run Ios Simulator

From my test, it seems that the load command LC_ENCRYPTION_INFO[_64] is also marked as PLATFORM_IOS. So I have to patch 3 kinds of load commands to mark the macho as PLATFORM_IOSSIMULATOR:

  • Remove the load command LC_ENCRYPTION_INFO[_64]
  • Remove the load command LC_VERSION_MIN_XXX
  • Patch the platform to 7 (PLATFORM_IOSSIMULATOR) in the command LC_BUILD_VERSION

From my test, I can directly launch the iOS App from the Simulator after the patch, if SIP is disabled. And I have to re-sign it with ad-hoc (free developer) if SIP is enabled.

Macos Run Ios Simulator Full

Through the effort before, I can launch the iOS App from Xcode Simulator successfully.

But there are some known issues for some specific Apps:

  • Some iOS App Extensions process crash
  • Crash due to lack of sandbox entitlements
  • Maybe other issues for specific App

I have tried to patch to PLATFORM_MACOS directly:

  • There is no problem for iOS command line program, and it is useful when you need to run iOS command line program on the M1 Mac.
  • For iOS UI Application, we need to use environment variable DYLD_FORCE_PLATFORM=2 to help us load UIKit.framework from /System/iOSSupport directory.

Macos Run Ios Simulator Free

Next are the test results for arm64 macho loading :

Ios Simulator Online

  • Arm64 executable process can load arm64e dylib directly.

  • Arm64e executable process cannot load arm64 dylib.

    Patch cpu subtype to 0x80000002 can bypass the platform check to load it successfully.

  • macOS process cannot load iOS platform dylib, error: mach-o, but not built for platform macOS

    Just patch the load_command 0x25=LC_VERSION_MIN_IPHONEOS to 0x24=LC_VERSION_MIN_MACOSX