iOS 15/16 Tweak Tutorial in Swift & Orion - Making InstantCamera

iOS 15/16 Tweak Tutorial in Swift & Orion - Making InstantCamera

Hi! In this tutorial I will teach you how I coded InstantCamera. This tutorial is targeted towards a more advanced audience, and if you don't know how to program tweaks at all, I highly recommend my iOS 14 Tweak Development Beginner Tutorial or PinAnim tutorial:

iOS 14 Tweak Development: Beginner Tutorial
Tweak development can be hard, especially when you realize that all tutorials on it are from 2015. That’s why I decided to make an updated tutorial. I assume you know Swift or understand some programming concepts. If so, great! If you feel stuck or get some errors that are not
iOS Tweak Development Tutorial: PinAnim
In this tutorial we will be creating an iOS 14 tweak named PinAnim. I assume you’ve already completed the iOS 14 Tweak Development: Beginner Tutorial. If not, I highly recommend checking it out before continuing. Here’s the final result we want to achieve by the end of this tutorial: 0:

However, if you have already read that tutorial, let's get started!

The Goal & Plan

The goal of this tweak is to capture a photo as soon as the user taps the Camera shortcut button on the Lock Screen. The button looks like this, if you forgot:

The simplest way to do that, is to replace the Force Touch gesture. Then, we will have another tweak that is injected into the Camera app and calls a method to take a photo as soon as the camera is ready (there is still a slight varying delay).

We also need to know from the Camera tweak whether the app has been launched from the Shortcut button, or regularly (e.g. from Home Screen). In latter case, we don't need to instantly capture a photo.

Set up the SpringBoard project

Let's get started. cd to your projects directory, run the nic.pl script from Theos and fill out the information. This will be the project that gets injected into SpringBoard. Later we will need to also create a subproject for Camera. Don't forget to run make spm and open the project in your favorite IDE (I'm using Xcode for this tutorial).

💡
Since having two projects is confusing, I left footers on some screenshots saying in which project the code is written.

Inspecting using FLEXing

Now we need to know what exactly to hook. Let's start with the Shortcut button as it's the most logical thing to start from.

Bring up FLEX and select the button (You may need to move away some other views that are overlapping the button):

💡
Inspect from Notification Center instead of LS. That way your device will not fall into sleep.

You will find that the class for buttons is CSQuickActionsButton and for the parent view it's CSQuickActionsView. Try finding a method that could be hooked to make the button not have a long delay until Camera app finally opens. (Due to limitations of the blog editor I'm writing this in, I cannot hide that image below, so don't look at it just yet if you don't want to spoil the answer)

Hint

There is a method that is executed when user begins a touch.

Hint 2

It's in CSQuickActionsView

Solution

The method we are looking for is -(void)handleButtonTouchBegan:(id)arg1 ;. We can hook it, and check if the button's type corresponds to camera's and call -(void)handleButtonPress ; right away.

Instantly open Camera app on short button touch

In SpringBoard project

I create a new global variable here shouldCapturePhoto – we will need it later to differenciate shortcut button press and regular app opening.

You also need to add headers in Tweak.h file:

In SpringBoard project
💡
RemoteLog.h is a useful remote logging utility, you might want to set it up using the tutorial I linked below. It is purely optional, just makes debugging easier.
How to setup RemoteLog in Swift/Orion
RemoteLog by Muirey03 is a useful utility for debugging your tweak. Since you can’t connect Xcode’s debugger (LLDB) and can’t use print you either have to use: * NSLog * Logger, which works only with static values. * UIAlertControllers, which are convenient for small text, but if there is a huge…

If you encounter a symbol(s) not found error, make sure to add CoverSheet private framework to PRIVATE_FRAMEWORKS variable in the Makefile. Insert a new line after CFLAGS in Makefile of Camera project – InstantCamera_PRIVATE_FRAMEWORKS = CoverSheet

Compile the project using make do THEOS_PACKAGE_SCHEME=rootless and now the camera button should open Camera app instantly.

Switching to "photo" mode

We need a way to switch to photo capture mode as soon as the app opens. Try searching for a method in one of UIViewControllers inside Camera. If you don't know the name of view controller, you can select one of the views in the hierarchy, tap "i" and select "Nearest View Controller":

Hint

UIViewController that contains the method we are looking for is in CAMViewfinderViewController

Solution

-(void)changeToMode:(NSInteger)arg0 device:(NSInteger)arg1 animated:(BOOL)arg2 ; inside CAMViewfinderViewController

💡
Alongside the found method, there is also -(BOOL)capturePhoto;, which we will need later.

Set up the Camera project

cd to your project directory and run nic.pl once again. Choose a different identifier, for example net.sourceloc.instantcamera.camera. Target process should be Camera with the first letter capitalized. Bundle Filter should be com.apple.camera, as that is the Bundle ID for the Camera app.

Now with the project setup, lets hook the app delegate's applicationDidBecomeActive, so the code gets executed on every app enter, and execute the found method mentioned in the "Solution" drop-down from there:

In Camera project

However, just like we did before, we also need to check if app was opened using the Shortcut button. Remember how I mentioned shouldCapturePhoto earlier? The easiest method would be to send a notification from Camera to SpringBoard, signaling that the app has entered foreground, and then SpringBoard should respond with another notification if Camera needs to take a photo. It's a workaround, and I'm sure there's a better way to do that using semaphores, but I don't know how :P

In Camera project

I'm temporarily using a delay of 1.0 seconds here with asyncAfter(deadline:exeute:). We will change that in a moment.

To be able to access viewfinderViewController you will need a property defined in Tweak.h:

In Camera project

And in SpringBoard project:

In SpringBoard project

Compile the project, press the camera button and the phone should first switch to Camera mode and shortly after take a photo... but not instantly :) Let's find a method that gets executed when the camera is ready.

Capture photo when the camera is ready

This method took me the longest to find. I've been searching for methods with names "ready","blur", "opacity", "alpha", "switch" in all sorts of UIViewControllers and in UIViews inside Camera. And I finally found something that looked promising inside CAMApplication:

I found it by decompiling the entire CameraUI's framework binary in Binary Ninja (could've been done just by using GitHub Search, but I was already in the decompiler at the time) and searching for "didChange":

Here's how it could've been found if I used GitHub Search (using qingralf's dumped iOS 16 headers):

Let's try logging something using RemoteLog (i have the wrapper function called log)

And sure enough we see the output once the mode has been changed and camera is ready to take a picture:

Let's execute take the delegate, cast it and capture a photo:

Make sure to also remove the temporary delay we made before:

Let's compile aaaand...

Error. Just like before, insert a new line after CFLAGS in Makefile of Camera project – InstantCameraCamera_PRIVATE_FRAMEWORKS = CameraUI

Compile again and you now the tweak should be complete. Try taking a photo by using the shortcut button on Lock Screen!

💡
Massive W

credits to icraze for bootloopware