Evan Boehs website Mastodon PGP Key email A drawing of an astronaut in space United States is never gonna tell a lie and hurt you

Lies under oath: "app store levels playing field and supports developers"

in
blog
tags
date
1/26/2024

In 2008, John Gruber published a blog post titled Private. The post, published less than 6 months after the App Store’s release, detailed a number of rejections from the store on the grounds of private API usage. Private APIs are APIs that can be called from iOS apps, but developers are not allowed to use them. These APIs, then, exist souly for use within Apple’s own apps. Gruber’s post was seemingly written in a different political climate than that of 2024. His post, and the posts he made reference to, seem convinced that Apple reserves the right to treat developers exactly as they wish. An Ars article, written a month earlier, states:

Apple is a private company. It can do whatever it wants to stock its App Store shelves however it pleases. There’s nothing that says the company must treat Joe Q. Developer the same way it treats Moneybags J. Google.

This quote, though Erica Saudun could not have known it at the time, aptly describes the same arguments being lobed in defense of Apple to this very day. In this article, I will not pass my own judgements on the validity of these arguments. Instead, I wish to present a holistic view of the situation, one from the perspective of an indiependent developer of apps for Apple platforms.

The range of information accessible through private APIs is large and ever growing – from certain cosmetic elements to the private information of iOS users. Initially, the restrictions on private APIs were enforced through simple visual tests. That changed when, in 2015, SourceDNA revealed that some apps were using private APIs to read the email address & other information of iOS users without their consent. After this revelation, SourceDNA was acquired by Apple. Their static analysis technology was integrated into App Store review, and Apple began increasing the degree of access controls on these APIs. Still, in 2024, private API usage is still rampant, developers have simply learned how to fool static analysis. Below is a snippet of MIT licensed code by Andrew Zheng, a prolific iOS developer:

let filterClassStringEncoded = "Q0FGaWx0ZXI="
let filterClassString: String = {
    if
        let data = Data(base64Encoded: filterClassStringEncoded),
        let string = String(data: data, encoding: .utf8)
    {
        return string
    }
    return ""
}()
// snip //
guard let filterClass = NSClassFromString(filterClassString) as AnyObject as? NSObjectProtocol else {
    return
}

This code obfuscates the name of the Private API being called by encoding it via base64, and decoding it at runtime. It’s safe to say that, as long as private APIs exist, developers will find a way to use them. What private API is this code using? The API in question, CAFilter implements the variadic blur that is visible in first party iOS apps, like Apple Maps.

Despite arming developers with sanctioned design tools, Apple always likes to keep their proudest design elements exclusive to their own apps. There is, of course, a case to be made in Apple’s favour here. Is Linear, for instance, required to open source their design system? Fine. Keep the blur to yourself, but what about system APIs?

I am working on an app for rating music. It would be convenient to offer a way to quickly open the currently playing album in my app, agonistic of the media player in use. A quick google search yields this answer on StackOverflow. For postarity, the code is copied below:

The code
let bundle = CFBundleCreate(kCFAllocatorDefault, NSURL(fileURLWithPath: "/System/Library/PrivateFrameworks/MediaRemote.framework"))
guard let MRMediaRemoteGetNowPlayingInfoPointer = CFBundleGetFunctionPointerForName(bundle, "MRMediaRemoteGetNowPlayingInfo" as CFString) else { return }
typealias MRMediaRemoteGetNowPlayingInfoFunction = @convention(c) (DispatchQueue, @escaping ([String: Any]) -> Void) -> Void
let MRMediaRemoteGetNowPlayingInfo = unsafeBitCast(MRMediaRemoteGetNowPlayingInfoPointer, to: MRMediaRemoteGetNowPlayingInfoFunction.self)
guard let MRNowPlayingClientGetBundleIdentifierPointer = CFBundleGetFunctionPointerForName(bundle, "MRNowPlayingClientGetBundleIdentifier" as CFString) else { return }
typealias MRNowPlayingClientGetBundleIdentifierFunction = @convention(c) (AnyObject?) -> String
let MRNowPlayingClientGetBundleIdentifier = unsafeBitCast(MRNowPlayingClientGetBundleIdentifierPointer, to: MRNowPlayingClientGetBundleIdentifierFunction.self)
MRMediaRemoteGetNowPlayingInfo(DispatchQueue.main, { (information) in
    NSLog("%@", information["kMRMediaRemoteNowPlayingInfoArtist"] as! String)
    NSLog("%@", information["kMRMediaRemoteNowPlayingInfoAlbum"] as! String)
    // snip //
})

With modification, the code above correctly provides the necessary information for my app’s functionality. Despite this, my app will never run it. Users will always need to search the album they wish to rate. Users will always have a poor experience, despite the fact that just 10 lines of code can fix it. That is because this code, of course, makes use of private APIs. I could attempt to obfuscate my usage and pray, or I could settle for a poor user experience. Because a rejection would offer me no recourse, I choose the latter.

It’s not just APIs that only Apple gets to play with. In addition, in Spotify’s 2019 Play Fair campaign, Spotify alleges that Apple has placed itself at an unfair advantage, as they do not need to offer 30% of their revenue to themselves. By competing in Spotify’s court, while forcing Spotify to conform to their rules, Apple subjects Spotify to a playing field that is certainly not level. Level playing field… Where have I heard that before?

Oh. It might have been 2021’s Epic v. Apple lawsuit, where Epic made similar complaints about the 30% tax. Epic argues that this tax is unfair, as they are forced into Apple’s distribution platform because they cannot distribute Fortnite through external sources.

One year before this suit, as part of an antitrust hearing on the app store, Phil Schiller commented to reuters that:

One of the things we came up with is, we’re going to treat all apps in the App Store the same - one set of rules for everybody, no special deals, no special terms, no special code, everything applies to all developers the same.

I would be remiss if I did not call attention to the deliberate language here. Note that Phil did not state they intend to treat all apps the same, he said they intend to treat all apps in the app store the same. Despite the fact most of Apple’s apps are available in the store, I suspect the fact that they are preinstalled on user’s devices carves out a convenient exception. Then again, Phil is a lier. That statement, this idea that they treat 3rd party apps the same, is bullshit.

In 2008, an update to the Google Mobile app added the ability to lift your phone to your ear and speak to search. The API to add this capability is private. In John’s article on the matter, in 2008 mind you, he states

Third-party iPhone development is purportedly a level playing field. If regular developers are forced to play by the rules, but Google is allowed to use private APIs just because they’re Google, the system is rigged.

Rigged indeed.

In 2015, around the same time it came out that 250 Chinese apps were using private APIs to read user emails and serial numbers, it was discovered that Uber was using the same IOKit private API to fingerprint their users. (primary source)

Those Chinese apps were removed from the store. Kalanick went to Apple HQ “sporting his favorite pair of bright red sneakers and hot-pink socks”, where Tim Cook asked him “in his calm, Southern tone” to stop.

Given Uber’s history of bending Apple’s rules, you might be shocked to learn that in 2017, Uber was granted the com.Apple.private.allow-explicit-graphics-priority entitlement. This entitlement gives Uber’s app direct access to the IOMobileFramebuffer. This means that, for some amount of time, Uber had the ability to directly read and write to the iPhone’s framebuffer, including in the background, meaning that they could record the screen at any moment. Uber claimed this was required to render maps on the Apple Watch at the time. Spotify complains that Apple wasn’t even letting them make a watch app.

Remember Phil’s comment about a level playing field? A little less than a year later, the Epic v. Apple suit gave us some juicy documents. In a presentation marked Apple Confidential–Internal Use Only, we learn that, in an effort to retain Netflix, Apple built Netflix custom APIs to manage billing within their app, as opposed to going though the app store. They also offered to build additional features based on Netflix’s requests. Similar APIs were offered to Hulu.

Zoom’s iPad app was granted the com.Apple.developer.avfoundation.multitasking-camera-access entitlement, which is not documented. This entitlement allows Zoom to… you guessed it… use the camera while multitasking. A developer of a 3rd party Zoom app was curious how Zoom was able to do this. Well, the answer is preferential treatment.

Recently, when Elon Musk rebranded Twitter to X, he faced difficulty on the iOS app. As Taylor Lorenz notes, the App Store disallows single letter app names. Why then, was Twitter’s name eventually updated to X? For some reason, Apple caved to Musk, carving an exception in the App Store policy out for him.

Why then, in the 2020 antitrust hearing, did Tim Cook say under oath that

We treat every developer the same. We have open and transparent rules, it’s a rigorous process. Because we care so deeply about privacy and security and quality we do look at every app before it goes on. But those rules apply evenly to everyone.

If Cook cares so much about privacy, why did Uber get the framebuffer? If all apps play under the same rules, why is Twitter now named X? Is it possible? Did Tim Cook commit a felony? All signs point to yes.

Yesterday, in a blog post laden with resentment, Apple Inc. announced the measures they are taking to comply with the European Union’s Digital Markets Act. The Digital Market Act (DMA) is a piece of legislation aimed at big tech operating in EU jurisdiction. The intention of the bill is to provide room for businesses to operate and compete in fields dominated by these companies.

To comply, Apple has announced new terms that can be opted into. These new terms, possibly in violation of the DMA, grant additional freedoms to developers, including a reduced commission. Problematically, however, opting into these new terms means after 1 million users, each user will cost the developer 50 cents a year, making the new terms effectively useless. One pro of these terms, however, is the interoperability request form. If Apple faithfully adheres to said form, it’s possible their lie of a level playing field might just come true, at least for developers in the EU. That’s a big if, however. I somehow doubt, given Apple’s 17 year long history of shafting developers, that they will. But they might, I suppose. They might. Keep hoping.

/node/private-apis.html