How to detect screenshots on iOS (like SnapChat)

A number of Stack Overflow questions were having issues with this, so I figured I’d explain. From reverse engineering, this is the exact method used by SnapChat, but it’s also pretty much what I’d have done myself.

The process is pretty simple, and relies on a quirk of iOS: taking a screenshot cancels all touches on the screen. Because of that, anything that you want to protect will require you to have the user to touch the screen to see. If that works for your purposes, the general solution is to simply intercept the touch cancellation, and quickly remove any sensitive information from the screen. If you’re also implementing a screenshot counter, as with SnapChat, you will also need to take into account the other cases when a touch might be cancelled: from a system gesture (Notification Center or the iPad’s multitasking gestures), or by activating other pieces of system UI (the power down menu, or the multitasking switcher).

On a technical level, the two basic pieces are UILongPressGestureRecognizer (or -touchesCancelled:withEvent:, if you want) and UIApplicationDelegate. In your long press handler, you should hide your sensitive information when the gesture recognizer’s state is UIGestureRecognizerStateCancelled, and if you want to track the number of screenshots, increment a counter. Then, in the UIApplicationDelegate, decrement that counter when you receive the -applicationWillEnterBackground: or -applicationDidResignActive: notification to account for the other possibilities for a cancelled touch. You might also need to handle other situations where a touch could be cancelled, if other parts of your app might cause that to happen.

Introducing Infiniapps 2.0

It’s been a long time now, but the first release of Infinidock was almost three years ago. It was only my second tweak for iOS, after ProSwitcher with Ryan Petrich, and my first in the Cydia Store. The first version was built in just one week after coming up with the idea, and only worked on iOS 3.0 and iOS 3.1.

Since then, it’s gained a number of extra features, and I also released Infiniboard that February and Infinifolders later that year. But all three have still been based on the same code I wrote in one week during January 2010. Because of that, adding new features and supporting newer iOS releases was growing more difficult with every update. It was time for a clean slate.

And so today I’m releasing new versions of all of the Infiniapps, rebuilt from the ground up for iOS 5 and iOS 6. But while the core is brand new, all of your favorite features, settings, and purchases are still there, and everything should seamlessly carry over into the new versions. I also added a few new features — scrollbars and bouncing settings are now an option in Infinidock, for example — and new features should be much easier to add in the future.

Another big focus was fixing a number of longstanding bugs, for both stability and performance. Also gone is a little secret I’ll let you in on: until now, the Infiniapps were never really unlimited. There’s always been a hidden cap (for performance reasons), somewhere between 50 and 200 icons. With the new architecture, though, performance is barely affected by the number of icons, and I’ve made it truly unlimited. Compatibility with other extensions should also be better; Gridlock, for example, now works without any special support or cooperation from my other extensions.

Infinidock, Infiniboard, and Infinifolders 2.0 are in Cydia now!

If you’re jailbroken, be sure to go check them out.

As always, if you have any issues or questions, feel free to contact me through the “email author” button in Cydia. I’ll get back to you as quickly as I can.

Besides the Infiniapps, I also have a few other updates to talk about:

  • VoiceActivator, my Voice Control extension, was also recently updated to support iOS 6.
  • Zephyr has supported iOS 6 since day one, but I’ve released a small bug fix update to better support task switcher modifications.
  • Last but not least, I’m still working on a Gridlock update for iOS 6 — hopefully it will be ready to release soon.

Building for armv6 in Xcode 4.5

The original iPhone, iPhone 3G, and first two generations of iPod touch used processors supporting the armv6 instruction set. However, with the iPhone 3GS, Apple moved to the more modern, but backwards compatible, armv7 instruction set, and the iPhone 5 includes armv7s. To support older devices while taking advantage of the capabilities of the newer processors, iOS supports fat binaries, which are multiple executables, each for a separate instruction set, combined into one.

But with the latest release of Xcode (as I write this, version 4.5), Apple removed support for building armv6 components, essentially forcing developers to drop support for iOS releases up through iOS 4.2.1, the last supported version on the armv6 devices. I like supporting older devices for a long time, though, so dropping support for anything before iOS 4.3 so soon isn’t what I would want. Luckily, however, with a minimal amount of hacking, you can convince Xcode 4.5 to build your code for armv6 and for iOS 4.2.1 and below — as long as it doesn’t build against the iOS 6 SDK.

Here’s how to do it:

  • Find a copy of an older version of Xcode. If you’re a member of the iOS Developer Program, this is easy: just download Xcode 4.4.1 from the developer download site. If you’re not, Google is your friend.
  • Extract the iOS 5.1 SDK from the older version of Xcode: open up’s contents, and copy out the Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/ directory.
  • Add the SDK to Xcode 4.5. This is the reverse of the above: you’re just adding that SDK next to the existing iPhoneOS6.0.sdk that comes with Xcode 4.5. If you use Finder’s “Go to folder” command, you again want Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/.
  • Open your project in Xcode, and view its properties. If you look under “Build Settings”, there is now a new option under “Base SDK”: iOS 5.0. Choose that.
  • Still under “Build Settings”, find the “Valid Architectures” row. Since we are building with the iOS 5.1 SDK, we won’t be able to build for armv7s (iOS 5.1 was before the iPhone 5 was announced), but we can build for armv6, so edit it and swap armv7s for armv6.
  • Now, you’ll be building for armv6, but your apps still won’t run on any armv6 devices, since the deployment target is still something at or above iOS 4.3 — which no armv6 devices can run. Under “Info”, change that to whatever target you want, from iOS 3.0 or later. You won’t find that version in the drop down, though, so you’ll need to type it in manually.

And that’s all there is to it. It’s not perfect: you can’t use the iOS 6 SDK when using this trick, and you can’t build for armv7s. And you might need to re-add the iOS 5.1 SDK after you update Xcode through the App Store. But you can use one set of developer tools to build both iOS 6 apps and older ones, which is likely enough if you’re bothering to support the iPhone 3G in the first place.

IDA Pro: Lion Fullscreen

I’m a big fan of OS X Lion’s full screen mode, and I run almost all of my apps in full screen. However, until recently, IDA Pro, one my most used apps, has been impossible to get running in full screen — Lion only supports Cocoa apps running in full screen, and through version 6.2, the Mac Qt port of IDA Pro used Qt’s Carbon backend. IDA 6.3’s announcement included a mention of full screen support, but it wasn’t the native Lion full screen that I’ve been wanting.

However, with IDA 6.3, the backend has switched to Cocoa, and so adding support for full screen mode is now possible. While IDA itself does not include the workaround to natively add full screen support, with its plugin support, we can add it ourselves. (I’m pretty sure that my Maximizer would work here, too, but it both requires SIMBL and loads into every app on the system.)

My simple plugin is available for download. It does, however, require a few notes:

  • IDA only loads plugins after a file is opened, so you can’t enter full screen until you have loaded a .idb or binary.
  • IDA wasn’t designed to support this mode, so you might have some graphical issues. Usually, just exiting full screen mode has worked for me, but it’s certainly possible you might lose unsaved work.
  • To install, as with any IDA plugin, just drop it in the plugins folder in

The source code is pretty basic (and compiles with quite a few warnings), but I’ll post it if anyone is interested in seeing how this was done.

iOS 5 Status

Here’s the status of all my stuff on iOS 5 (all versions: iOS 5.0, 5.0.1, 5.1 and 5.1.1). If you haven’t heard of any of these before and want to check them out: search in Cydia, they’ll all show up. Feel free to send me a support email from Cydia if you have any issues or questions, for paid products I try and respond within a few hours if I’m not sleeping.

I’m going to keep this post updated for all the latest status updates to each of them, so be sure to check back here.

Paid Extensions

  • Zephyr: Full iOS 5 support.
  • Gridlock: Full iOS 5 support.
  • VoiceActivator: Full iOS 5 support.
  • Infinidock: Full iOS 5 support. (As of version 1.8, available now in Cydia!)
  • Infinifolders: Full iOS 5 support. (As of version 1.4, available now in Cydia!)
  • Infiniboard: Full iOS 5 support. (As of version 1.8, available now in Cydia!)

Free Extensions

  • IconSupport: Full iOS 5 support. (Via @ashikase, who did the iOS 5 work: thanks!)
  • AppSlide: Full iOS 5 support. (As of version 1.1, now available in Cydia!)
  • MobileVolumeSound: Full iOS 5 support.
  • Internalizer: Full iOS 5 support.
  • Five Icon Switcher: Full iOS 5 support.
  • Webscrollian: Full iOS 5 support.
  • No Page Dots: Full iOS 5 support.
  • No Bookmarks: Full iOS 5 support.
  • Full WebClips: Full iOS 5 support.
  • No Folder Badges: Full iOS 5 support.
  • Spire: Supports iOS 5.0 and 5.0.1, but not iOS 5.1 or 5.1.1.
  • Empty Folder Icons: Unknown. Let me know?
  • Colloquy Tab Complete: Unknown. Let me know?
  • Covert: Does not work, but similar functionality is included in iOS 5.
  • ListLauncher: Does not work.
  • Switcherscape: Does not work.


Since it was released on AT&T this weekend, I was able to try it out at a store today. I only played with it for a few minutes, but here’s what I thought:

  • It’s smaller than you think. Definitely still a big phone, but it’s thin, and doesn’t feel huge (like the Galaxy Note).
  • The screen is great. It doesn’t have the awful PenTile that Samsung still ruins their high-end phones with, and the density is as good as I want. Probably the best non-iPhone screen I’ve seen yet.
  • It’s fast. Definitely the fastest phone I’ve tried so far. And unlike every other Android phone I’ve used, it can scroll a list smoothly. (About as smooth as a single-core Windows Phone or iPhone. And the physics still suck. Sigh.)
  • HTC Sense is awful. I’m not a huge fan of Android 4.0, and I think it needs a lot of work, but it’s miles better than the crap HTC added here.
  • The hardware is solid. Not quite as solid as the Nokia Lumia 800 or 900 or the iPhone 4/4S, but definitely better than the Samsung phones.
  • The glossy case on the white model is suboptimal, and the dark gray model doesn’t look nearly as good. I’d love a matte white model, but HTC apparently decided against it.

Overall, this is the best Android hardware I’ve tried, but the software is even worse than the Galaxy Nexus’ stock Android. The Galaxy S III looks even worse: more awful skin and the usual ugly, creaky Samsung hardware.

Will there ever be an Android phone with hardware and software that anyone would actually want to buy?

How to test your app on older iOS releases

In this week’s Build and Analyze, Marco Arment talks about supporting older iOS versions in Instapaper. As he says, being able to test your app on those versions is not easy, and just getting a device to run a specific iOS release can need “hacky jailbreak tools” or can even be impossible. Since I spend my time with those (admittedly hacky and confusing) tools and have an understanding of how the restrictions here work, I thought I’d write an explanation and a guide on how to successfully test for older iOS releases.


Originally, there weren’t any issues with running any iOS release on your device. You just download the firmware (ipsw) file, open up iTunes, and restore with it. This method worked through iPhone OS 2.0, the iPhone 3G, and the second generation iPod touch.

However, with the iPhone 3GS, Apple changed everything. On that device (as well as the iPod touch (third generation), the original iPad, and the iPhone 4), each and every firmware change must be approved by Apple’s servers, at the time of the install. And Apple will only agree to let you install the current latest release of iOS at that time, which prevents downgrading — as well as any re-installs of the iOS release the device is running, as long as that release is not the absolute latest version available.

The iPad 2, iPhone 4S, and iPad (third generation) all use similar methods, with additional security measures. (I’ll talk about those later.) In addition, with iOS 4, similar protections were added to the iPhone 3G and iPod touch (second generation). While those devices can still be easily restored to pre-iOS 4 releases, it’s no longer easily possible to restore them to any version from iOS 4.0 on.

Why It’s Important

What Apple has done here, essentially, is sacrifice the ability for developers to test code on older versions of iOS, for the sake of increased control of how we use our devices. It’s hard to see a benefit: if someone is knowledgeable to downgrade a device, they probably also have a good reason to.

It’s unclear that Apple realizes what this does to developers. Xcode, even, doesn’t realize that this happens at all: the Organizer still has a file picker and version selector under “Restore”, oblivious to the fact that this option no longer, in fact, works (and hasn’t for almost three years). The enterprise iPhone Configuration Utility and education Apple Configuration tool are similarly confused.

A good, simple solution would be for Apple to simply allow devices registered for development to install older iOS releases, just as they are allowed to install betas. (To encourage Apple to fix this problem for developers, please dupe my radar and help call attention to it. Thanks!)

What To Do

Often, even if Apple doesn’t support it, you still do need to ensure your code runs on older iOS releases. Since you can’t trivially restore your device to an older version, I’ve made a list of the possible workarounds:

  • Use the Simulator

    This is definitely the easiest method. Rather than testing your app on a device, you just use the iOS Simulator to test it on that version. While Xcode itself only is bundled with the simulators for the latest two iOS releases, it’s pretty easy to import older ones: just drag the SDK bundle (iPhoneSimulatorX.Y.sdk) into the iPhoneSimulator.platform/Developer/SDKs folder. If you didn’t keep around copies of the older SDK, they are very findable online. Update: for registered iOS developers, you can apparently find old versions of Xcode you can extract the SDKs from available on Apple’s developer download page.

    Update: It’s even worse now, Apple appears to be blocking the iOS 4.3 simulator on Mountain Lion. I haven’t tested it, but you might be able to install it manually.

  • Save Devices

    The easier method, although the most expensive, is to simply begin a collection of devices running various iOS releases. Most developers I’ve talked to take this route, but it’s hard to enter. First, it’s expensive to buy all the iOS devices necessary to test each release. Secondly, it’s quite difficult to obtain a complete spread of iOS releases to test on. Since downgrading is blocked, you have to purchase these devices still running older releases — something that gets harder every day, as more older devices break and get replaced.

    And, once you have the devices, you have to be very careful to never break them — or to accidentally accept the endless stream of update prompts from iTunes. Remember: even if you just need to restore the device to the same older version of iOS as is already on the device, Apple doesn’t even allow that. So be very sure that they never get messed up.

  • Evading SHSH (the “hacky jailbreak tools” method)

    When Apple accepts your request to restore a device, they give back a cryptographic agreement to allow it, called an “SHSH blob”. In the iPhone 3GS, iPhone 4, original iPad, and iPod touch (third generation), however, Apple forgot to include one crucial piece of data in these signed blobs: a time range of when that acceptance was valid for. Using that mistake, Jay Freeman (saurik) developed a massive man-in-the-middle attack: storing the authorized responses for millions of device/firmware combinations, and replaying them to restore the device on demand.

    If did happen to store your “SHSH blobs” for a specific iOS version with the Cydia server for one of these devices when Apple was still signing that release (most jailbroken users probably did), you need to do some tricks to use the saved response.

    The tools for this are TinyUmbrella and redsn0w. First, use TinyUmbrella to download the SHSH files saved for your device. (Be sure to have “Request from Cydia” checked so it asks Cydia’s server, rather than Apple, for the saved blobs.) Then, use redsn0w to create an IPSW you can restore in iTunes.

    To save SHSH blobs for the future, also use TinyUmbrella, just have it ask Apple (uncheck “Request from Cydia”). If you didn’t manage to save this data but do have one of these devices on an older iOS release but want to extract the data for the version it is running, it is actually possible to do so. Use redsn0w and choose “Fetch” to copy the blobs off the device.

    Unfortunately, this loophole was essentially fixed in the iPad 2, iPad (third generation), and the iPhone 4S. (It may still be possible with GSM and Wi-Fi only models of the iPad 2, but don’t rely on it.)

Apple, please help!

The only way for this issue to be truly resolved for developers is for Apple to create a supported method to downgrade devices to test apps. While the iOS 5 over-the-air updates do appear to increase update penetration and speed, it’s simply not possible for many apps to only be available (or, worse: only tested) on the very latest version of iOS.

Right now, it does not seem likely that Apple will change anything here, so we are stuck with the workarounds above. However, there’s a chance that they will, and more people letting them know it’s an issue can only help.

Let me know if you know of any other workarounds or things I missed: I’m on Twitter as @chpwn, or you can email me at

Page Turning

This is how you turn the page of a book:

This is how Apple’s iBooks turns the pages of a book:

This is not how you turn the page of a book:

This is how Google Boo— oops, sorry, Google Play Books — turns the pages of a book:

This is a good example of how Google manages to only make it around 70% of the way to a good UI. They often get the basic idea right, but the details (like, for instance, “does anyone actually turn a book this way?”) slip through, and it shows.

It’s why I’ve ditched the new Gmail web UI for Sparrow, even though it’s missing many features I liked and actually runs worse on my laptop. And it’s why I’ve recently switched my browser to (gasp) Safari: after a year from the first betas of OS X Lion, it still only had “most” of what you would want from a full Lion-ready browser.

I certainly hope Google finds a way to solve this issue. Most of their products are ahead of the competition in other respects, and I’d love to use them. But if Google still can’t make it all the way, even with a new focus on design, I’m not going to shove an inferior experience on myself.

See also: ICS Paper Cuts.

Ski Trip to iOS 3.1.2

Last weekend, I went skiing at Northstar — the codename of iOS 3.1. (Each iOS release is codenamed after a ski resort, usually in Tahoe or Colorado.) I don’t have anything to match comex’s awesome PDF slope slope from JailbreakMe 3.0, but it wasn’t all bad:

Springboard ski slope

SpringBoard was there, right near the top of the mountain. Also, there was a new lodge this year, to go with one of my new tweaks:

Zephyr Lodge

But, sadly, it was closed. Maybe that’s because Zephyr doesn’t support iOS 3.1. ;P

Spire Proxy “FAQ”

Spire needs a proxy!? It’s useless!

As has been noted, my recently released tweak Spire requires a proxy and data from an iPhone 4S to operate. As the point of Spire is to get Siri on devices that aren’t the iPhone 4S, this might at first seem to imply that Spire is useless.

However, this is not because I hate you or because I’m lazy, it’s because Apple has made it very likely impossible to defeat the authorization requirement. I reverse engineered it, and it does not appear possible to connect Siri to the cloud without information from an iPhone 4S.

However, that doesn’t necessarily mean that Spire is therefore useless.

So, how do I get a proxy?

There’s any number of ways for you to get a proxy that will help you connect Siri to Apple. Here’s a few of my ideas:

  • westbaer’s SiriProxy fork
    • Own an iPhone 4S too: Maybe you already own an iPhone 4S, and just want Siri on another device of yours. This is simple; you can just use the above proxy yourself.
    • Find a friend: Maybe your friend has an iPhone 4S and will let you use their authentication tokens (maybe in exchange for some cool SiriProxy plugins). Then, you can share the authentication. Or, maybe you gave your relative your old iPhone when you got your iPhone 4S: now you can share your token and give them Siri.
  • Pay up: It’s very likely that soon we will see for-pay services online to rent you some space on a Siri proxy, attached to one of their iPhone 4S devices. I haven’t seen anything like this yet, but I’ll keep my eye out, and I would encourage anyone who is interested to set something like this up.
  • And now for something completely different: As I suggested earlier, you might be able to replace Siri entirely. A simple method might be to use Google Chrome’s speech “API” hooked up to some code to decode the Siri requests and parse Google’s result. Or, someone could hook it up to some logic backends like many of the clones available on Android: the possibilities are endless.

Okay, I’ll find something!

Good! Spire is far from perfect, but at least at this point in time, it’s the best that I can do. Maybe in the future someone will find a way to evade the authorization requirement, but from my position here that’s unlikely. Hopefully, however, you’ll be able to figure something out.