iOS Tweak Development | Andy Ibanez https://www.andyibanez.com Teaching you cool stuff since 2012. Mon, 19 Aug 2019 22:08:54 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.10 39653125 Creating Aggregate Projects on Theos: A Configurable Tweak. https://www.andyibanez.com/creating-aggregate-projects-theos-configurable-tweak/ https://www.andyibanez.com/creating-aggregate-projects-theos-configurable-tweak/#comments Sat, 11 May 2013 02:32:08 +0000 http://andyibanez.com/?p=122 Learn how to create aggregate projects with Theos, while writing a configurable mobile substrate tweak.

The post Creating Aggregate Projects on Theos: A Configurable Tweak. appeared first on Andy Ibanez.

]]>
Creating Aggregate Projects on Theos: A Configurable Tweak.

More often than not, you will want to create a Theos project that is composed of one or various subprojects. A very common case for this would be to write a Tweak that can be configured by the user (guess what we’re writing today…): You’d create a Tweak project that contains a PreferencesLoader subproject that is in charge of writing the tweak’s behaviour. Aggregrate projects with Theos are very easy to build.

In this tutorial we’re going to write an Aggregate project that does just that: We will write a very simple tweak with a simple PreferencesLoader that will decide whether the tweak should alert the user when the app has been launched or not.

You have seen this simple tweak in action before: I wrote it in my Writing a MobileSubstrate Tweak for iOS tutorial. In fact to save us the time, I will assume you followed that tutorial, and that you managed to compile it and run it. We will write the same tweak with a twist. In that way we will save us the trouble of writing a MobileSubstrate tweak and get straight to the point of this tutorial.

In this tutorial…

  • You’ll learn the basics of a PreferencesLoader project (Note that this is NOT the same a Settings.bundle found in App Store apps, but they can share similarities).
  • You will learn to write aggregate projects with Theos. An aggregate project is a project that contains one or more sub projects.

The following assumptions are made:

  • You know how to write iOS apps. You know the SDK and you have written an app. It doesn’t matter if said app has been published. You have the necessary skills to write an iOS app.
  • You know how a Settings.bundle works and its limitations for iOS Apps. You also know how to make bundles and edit their plists manually.
  • You know how to write Mobile Substrate tweaks. If you don’t, head over to my tutorial to get started…

Alright, without further ado, let’s get started!

Writing The Mobile Substrate Tweak: Aggregate Projects Don’t Come Alone!

Since we’re going to build this project on top of an old tweak from an old tutorial, go build the tweak described in the tutorial linked above. It shouldn’t take you long (it’s literally less than 15 lines long), so go ahead and build it. I will wait for you.

Got it up and running? Cool, let’s continue.

Discussion on PreferencesLoader: The Most Common Use For Aggregate Projects

PreferencesLoader was written by DHowett as a way to let developers create settings pages for their apps and tweaks in the Settings app. PreferencesLoader can be very different than the standard Settings.bundle you’re used to.

When it comes to PreferencesLoader, there are two approaches to fetch to create a settings page for your app: The simple approach and the PreferencesLoader approach.

The Simple Approach

The simple approach is very similar to a Settings.bundle in that the cells are loaded from a plist file stored in /Library/PreferenceLoader/Preferences/. This plist contains the specifiers that your app requires. These can be simple cells that contain switches, text, text fields, and so on. The specifiers are loaded from their respective files and using the simple approach your bundle doesn’t execute code. It just loads and saves the values in the modifiable fields of the bundle.

The PreferenceBundle Approach.

Unlike the static settings the Simple Approach offers, this approach actually allows your settings bundle to execute code. The entry plist for this approach must have the same name of the PreferenceBundle to run and define it’s isController option to true. You can show your own view and everything you need. I will cover the PreferenceBundle Approach in another tutorial.

For more information on PreferenceLoader, refer to the iPhoneDevWiki page on the topic.

Creating Aggregate Projects: Getting Our Hands Dirty.

Now you have the tweak that alerts the user when an app has been launched. This is cool, very cool, but what if we wanted the user to have the ability to turn it off and on as he pleases? We need to write a settings page for it. But it wouldn’t make sense to write a different project for it. Does it make sense to ask the user to install the tweak, and then separately install it’s settings page? I don’t think so.

Aggregate projects come to the rescue. We’re going to create the settings project for our tweak, and they will be installed at the same time, since the settings project will be a subproject of the main project! But how do we do that? There is no template or anything to simplify the job of creating an aggregate project.

You don’t really need to do much to create an aggregate project. In fact, it’s just a matter of adding two extra lines to your makefile and that’s it.

But we don’t have the preferences project set up yet. In your terminal window, cd to your current project’s directory and run Theos’ nic.pl to create a new project. Select “iphone/preference_bundle” and finish the wizard.

If you’re lucky, Theos will automatically detect you created a project inside another one, and it will modify the makefile for now. If it doesn’t, don’t worry. Simply add the following two lines to it and you will be fine:

SUBPROJECTS += notifiersettings
include $(THEOS_MAKE_PATH)/aggregate.mk

It’s important the SUBPROJECTS specifier is before the include of the aggregate makefile.

Wiring Things Up: The Core Of Aggregate Projects.

make package install your project now. Open the settings app. Apart from our nice alert that tells the user Settings has been launched, if you scroll down a bit you should see a cell containing our settings preferences name for our tweak. Open it up and you should see this:

If you change the switch and go back, you will see its current state was automatically saved by our good friend PreferenceLoader. At least with saving settings, you don’t have to worry about that if you use the simple approach.

Naturally now we gotta modify its strings so it makes sense to the user.

Go to the PreferencesLoader subproject. Now open its Resources folder and open the plist there (NOT the one called Info.plist – the other one that has then name of your project). Since you know how to modify these, just change the name of the label to something like “Alert user on app launch”:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>items</key>
	<array>
		<dict>
			<key>cell</key>
			<string>PSGroupCell</string>
			<key>label</key>
			<string>Notifier Settings.</string>
		</dict>
		<dict>
			<key>cell</key>
			<string>PSSwitchCell</string>
			<key>default</key>
			<true/>
			<key>defaults</key>
			<string>com.AndyIbanez.NotifierSettings</string>
			<key>key</key>
			<string>alertLaunch</string>
			<key>label</key>
			<string>Alert User on App Launch</string>
		</dict>
	</array>
	<key>title</key>
	<string>NotifierSettings</string>
</dict>
</plist>

Now we just have to modify the actual tweak to read that value and alert the user if he so wishes (AKA if alertLaunch is YES).

With jailbreak apps you don’t have many “commodities” as you do with App Store apps, so you can’t use the same methods you’d use in App Store apps to read settings from a plist.

Instead, we have to read the plist manually, fetch the values we have to check for manually, and do whatever we want with those values.

You should know this by now, but in case you don’t remember, just create an NSDictionary object and use initWithContentsOfFile or dictionaryWithContentsOfFile. The full path to your settings plist is /var/mobile/Library/Preferences/YOUR_PLIST.plist.

In other words, your reading code would look like this:

	NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithContentsOfFile:
									[NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), @"com.AndyIbanez.NotifierSettings.plist"]];

(Note the use of NSHomeDirectory(). The iPhoneDev Wiki which is maintained by the guys behind our awesome jailbreak development tools recommend we never refer to /var/mobile directly, and that we should use NSHomeDirectory() instead.)

With that in mind, then you just read the value you want (alertLaunch in my case and probably yours too), and use it accordingly. When done, your whole method should look like this:

-(void)launch
{
    NSString *appName = [self displayName];
    
	NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithContentsOfFile:
									[NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), @"com.AndyIbanez.NotifierSettings.plist"]];
	NSNumber* shouldNotify = [settings objectForKey:@"alertLaunch"];
	
	if([shouldNotify boolValue] == YES)
	{
	    NSString *message = [NSString stringWithFormat:@"The app %@ has been launched", appName, nil];
	    UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:appName message:message delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
	    [alert1 show];
	    [alert1 release];
    }
    %orig;
}

And you’re done! Try it now. When the switch is on, you’ll be alerted when an app has launched. When it’s off, it will do the opposite.

You can downloade the source code for the project in my Github here. Study it and improve it, break it, etc..

If you have any questions or comments, you can as usual use the comments section below.

CHANGELOG:
_____________
May 10, 2013 at 6:00 PM [America/La_Paz]: First version of this post.
May 12, 2012 at 2:00 PM [America/La_Paz]: I forgot to add the source code for the settings plist. It’s added now.

The post Creating Aggregate Projects on Theos: A Configurable Tweak. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/creating-aggregate-projects-theos-configurable-tweak/feed/ 25 122
Writing SBSettings Toggles: A Tutorial. https://www.andyibanez.com/writing-sbsettings-toggles-tutorial/ https://www.andyibanez.com/writing-sbsettings-toggles-tutorial/#respond Sat, 24 Nov 2012 23:00:17 +0000 http://andyibanez.com/?p=119 Learn to write SBSettings Toggles easily using a Theos template. SBSettings Toggles are important in the jailbreak community. Learn SBSettings Toggles dev!

The post Writing SBSettings Toggles: A Tutorial. appeared first on Andy Ibanez.

]]>
SBSettings Toggles

In this tutorial, I will help you getting started with SBSettings Toggles, using Theos. Writing SBSettings Toggles is actually a really easy thing to do. SBSettings Toggles only have a handful of functions you must implement. The most trivial SBSettings Toggles don’t need any complex hooking, so teaching how to write them is not hard either. More complex SBSettings Toggles, like you may expect, need to either have some hooking or modify some system plists. If you have followed my other tutorials, you know I like giving theory first and then go practical by writing something. This case won’t be any different. Although the toggle we will be writing is going to be really, really simple: We will simply write a toggle that changes the display type in the Weather app (That is, when toggled, it will show in Celsius: When not, it will display in Fahrenheit).

Writing SBSettings Toggles? First, Understand the Theory.

If you’re here because you had problems reading BigBoss’ SBSettings Toggles Specifications, the first part of this tutorial is based on his theory. After all, I went to him to learn about SBSettings Toggles myself. Needless to say, I will explain things differently here, so if you had problems with the “Official Guide”, this one may still give you hope.

SBSettings is a modular application. All SBSettings Toggles are separate projects. The settings app looks for the togges in /var/mobile/Library/SBSettings/Toggles, and all SBSettings Toggles are given the name of the folder they are in. Toggles are said to be “ON” by default; They are said to be “OFF” when their folders contain a file called “OFF”. All SBSettings Toggles are actually dylibs that expose some C functions.

While writing SBSettings Toggles is not a hard thing and it has a small amount of required functions you must implement, there’s many things you must watch out for. Among these gotchas there’s things like memory usage and the methods that must be implemented. Let’s call them “the cardinal rules”. There’s 4 cardinal rules you should ALWAYS keep in mind when writing SBSettings Toggles.

The Cardinal Rules for Writing SBSettings Toggles

If you’re planning on taking this seriously, make sure you know these “Cardinal rules for SBSettings Toggles” by heart:

  1. All SBSettings Toggles run on SpringBoard. This has two major important implications: If your toggle leaks memory, the system will leak memory. And second, if your toggle crashes, the whole system will crash. Everytime I see people wanting to write jailbreak stuff, I tell them they must be willing to do it right, and I have emphasised this in my previous tutorials as well. If you can’t, or are not planning on doing it right, please stop reading this now. Otherwise you will release buggy tweaks that will be annoying for SBSettings Toggles users in the long run.
  2. SBSettings Toggles should not store data about their own state. Or do anything else that can take up memory for a long time, even if you’re not leaking it. Doing so will affect system-wide performance. You don’t want to slow down your user’s devices, do you?
  3. All SBSettings Toggles should avoid executing processes on disk with popen() or system() functions. These functions freeze SpringBoard when memory is really low (BigBoss states when memory is around or below 20 MBs).
  4. All SBSettings Toggles run as user mobile. This is important to know, because some functions need root access.

SBSettings Toggles Behind the Scenes: What Are the Functions They Need?

Like I have said before, SBSettings Toggles only need to implement a handful of functions.

Required Functions All SBSettings Toggles implement the following functions:

  1. BOOL isCapable()

    This function is called when SpringBoard loads. The purpose of this function is to tell the system whether it can run the toggle in question or not. If you need to check if the user is running a certain iOS version, device, or anything you can think of, this is the right place to do it. If you were writing a tweak that toggles the device’s flashlight, you would check if the device has a torch in this function. return YES if the toggle can be run; NO if otherwise.

    A quick gotcha with this function: If you don’t implement this required function, it will be assumed to return NO. In other words, make sure you implement it, otherwise your toggle will never run under any platform!

  2. BOOL isEnabled()

    This function is called when SpringBoard loads and when the Refresh button is tapped. Return YES if the toggle is enabled. NO if otherwise.

    Note that this function is to determine whether the tweak was enabled by the user or not; it’s not to determine whether the toggle is ON or OFF.

  3. void setState(BOOL enabled)

    This is where we toggle the actual “switch”. If the toggle is ON, ‘enabled’ will be NO (meaning it wants to turn it off) in the parameter. If the toggle is OFF, ‘enabled’ will be YES.

  4. float getDelayTime()
  5. This is the number of seconds, or partial seconds, that your toggle takes to “do its job” (AKA, how long does setState take to execute). This is the amount of time that the UISpinner will be visible after the user taps your toggle button.

Optional Functions:

  1. BOOL getStateFast()

    The isEnabled function takes a bit (a second or two) to determine whether all the SBSettings Toggles are enabled or not. Naturally, this is not acceptable, specially if the user shows the SBSettings window a lot. As such, you can implement getStateFast() to determine whether the toggle is enabled or not. isEnabled is only called when SpringBoard loads, getStateFast gets called everytime SBSettings is displayed. If you have code that can determine whether the toggle is enabled or not, you may place it here. Many times you can return the result of isEnabled here.

  2. void invokeHoldAction()
  3. This one’s a nifty and cool one. When the user taps and hold on SBSettings Toggles, they can reply to the event by calling this function.

  4. void setContainer(int Container)

    While it is optional, you will need it if you need an extra window for your toggle (SBSettings Toggles such as Fast Notes, Brightness, and processes use this). SBSettings will give you a 0 when talking about the SBSettings window itself and a 1 when talking bout the notification center. You will have to determine how you want to show this small window.

  5. void closeWindow()
  6. If your toggle created an optional window (such as the default Brightness toggle, that gives you a small window to set the brightness to whatever you want), you should implement this method to dismiss that view. This function is called when the SBSettings window is about to close.

  7. BOOL allowInCall()

    This determines whether your toggle can be used while in call or not. This is important, because some things such as a 3G toggle could drop the call if toggled.

SBSettings Toggles Can Call External Scripts and Functions From Within.

Before we go build those SBSettings Toggles I told you about at first, let’s absorb a little bit more of technology. After all, SBSettings Toggles are loved by everyone, and they can do much more than people expect.

This is SBSettings newest feature, and it is very nifty.

Although it is not directly, using this you can do many wonderful things. Instead of doing this directly, you can write an script that can be run by the SBSettings root daemon (sbsettingsd). To do this, you will send a notify_post message to the daemon. The daemon will take care of some details, including the fact that SBSettings Toggles cannot be run as root.

Okay, okay, but what is notify_post? notify_post is actually a BSD function, used for system-wide communication in BSD based systems. SBSettings Toggles that need to use the daemon should put files in the /var/mobile/Library/SBSettings/Commands directory. The filename of each file should be the post message. The nifty thing is that they can be compiled executables or simply scripts. Then, in your toggle, you will notify_post the name of your file; The daemon will receive this notification and run your file.

In this tutorial, we are not going to write SBSettings Toggles that call external scripts or resources. It’s another topic for another time, but, I will write “Part 2” of this tutorial and it will be focused on external scripts and functions. I mention this because it is important you know everything SBSettings Toggles can do, even if you don’t learn that yet (If you can’t wait for this, the BigBoss link I linked to above will take you to a small word on this. It writes a script that resprings SpringBoard – It’s really simple, but it should do the trick).

Getting Our Hands Dirty With SBSettings Toggles

The easiest SBSettings Toggles to write are those that modify plists that reside on your device. That’s the kind of toggle we will write here. You could also write a toggle that hooks code, or anything else that you may imagine!

Let’s get started with actual SBSettings Toggles programming! If you were on BigBoss’ site before, you may have noticed that he gives you a makefile and an initial main.m template. Luckily, there is a template that can be used with Theos so you don’t have to worry about the Makefile or anything. So go ahead and download this SBSettings Toggles NIC Template. Navigate to your Theos installation folder and save the template in templates/iphone. Courtesy of this template goes to WillFour20. If you want something more updated, and what I recommend, is the template created by the Theos man himself: Get SBSettings Toggles NIC Tar from DHowett’s Github.

Let’s create the project with the template now (You should have the hang of this by now: Open terminal, and open nic.pl in Theos’ bin folder to create the wizard).

SBSettings Toggles Wizard

If you’re using the template in DHowett’s repo, your template will look like this:

// Required
extern "C" BOOL isCapable() {
	return YES;
}

// Required
extern "C" BOOL isEnabled() {
	return YES;
}

// Optional
// Faster isEnabled. Remove this if it's not necessary. Keep it if isEnabled() is expensive and you can make it faster here.
extern "C" BOOL getStateFast() {
	return YES;
}

// Required
extern "C" void setState(BOOL enabled) {
	// Set State!
}

// Required
// How long the toggle takes to toggle, in seconds.
extern "C" float getDelayTime() {
	return 0.1f;
}

// vim:ft=objc

Don’t worry if you’re using the other template. Both templates will work fine.

You may have noticed that neither template implement all the optional functions. Don’t worry about it, because we won’t need them here, and if you ever need them for your personal projects, you can simply add them manually.

By the way, an “extra” requirement for SBSettings Toggles is that they must have an icon, although SBSettings will grab them anyway, but it will just show an icon with no glyph. If you want your icon to have glyphs, make them with a solid color (white or black are usually preferred). They are sized 60 x 60 points, and they are called on.png and off.png.

You can use these if you want (I just made them for this project – took a sec):

C-on C-Off

Note on using images: In order to use icons, your toggle must install the on.png and off.png images on each of the default themes that are included with SBSettings. At the time of this writing, the default themes are: Default, Default HD (retina: icons are 120×120 points), HUD, Serious SBSettings HD (retina), Tech Shadows, and Tenuis Matte.

To add your icons, create a “layout” folder in the root of your project. Anything that is placed here will be mirrored in the device’s / directory (thanks rpetrich for kindly giving me this note when I asked him why images won’t show up in my toggles). Every theme needs to have a directory with the name of your toggle and put the on.png and off.png images there. At the time of this writing, SBSettings themes are placed in “/var/mobile/Library/SBSettings/Themes”. So inside the layout folder, create a folder called “DEBIAN” (yes, that’s all caps), and place your control file (which is currently at the root of your project) there. Note that this DEBIAN folder is a “special” folder and it won’t show up in your device when you do make package install. Then, inside layout, make a directory called “var”. Inside it make a directory called “mobile”, and so on, until you have the following structure:

layout
	- DEBIAN
		- control
	- var
		- mobile
			- Library
				- SBSettings
					- Themes
						- Default
							- YOUR_TOGGLE_NAME
								- on.png
								- off.png
						- Default HD
							- YOUR_TOGGLE_NAME
								- on.png
								- off.png
						- HUD
							- YOUR_TOGGLE_NAME
								- on.png
								- off.png
						- Serious SBSettings HD
							- YOUR_TOGGLE_NAME
								- on.png
								- off.png
						- Tech Shadows
							- YOUR_TOGGLE_NAME
								- on.png
								- off.png
						- Tenuis Matte
							- YOUR_TOGGLE_NAME
								- on.png
								- off.png

For example, if you wanted the Default theme to contain your icons in this specific project, you would create a directory inside it and call it Celsius. It’s route will then be /var/mobile/Library/SBSettings/Themes/Default/Celcius, and you would drop your icons there.

You may have also noticed that themes don’t have on.png, on@2x.png, and so on images. They are either retina or they are not, so each theme only needs one ‘set’ of images.

Personally, I’m not a huge fan of this model and I think SBSettings could be really improved to make it both a breeze to expand and to theme. Currently it is a breeze to extend, but things like this make it a pain to theme. Hopefully this will change in the future.

To keep things simple in this project, we are going to leave the return value of isCapable to YES. Though for your specific projects you may or may not need to modify this.

Now we will go ahead and modify isEnabled. We are going to modify the weather app’s setting here. For that, you need to know that the file we will modify is called com.apple.weather.plist, and that it is located in /var/mobile/Library/Preferences. If you have studied prior iOS development before (YOU SHOULD HAVE!), you know how to open plists and to modify them with code. This plist has a property called “Celsius”, and it’s a boolean. If it’s enabled, it will display the weather data in celsius. If not, then it will show them in Fahrenheit.

So we are going to implement isEnabled now. For this toggle, “enabled” would mean the weather app is displaying it’s data in celsius. Implement it as follows:

extern "C" BOOL isEnabled() {
	NSDictionary *weatherSettings = [NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.weather.plist"];
	BOOL inCelsius = [[weatherSettings objectForKey:@"Celsius"] boolValue];
	return inCelsius;
}

Like I told you, the simplest of SBSettings Toggles just read and modify existing plists, but even then, they are very powerful. Here we read the weather settings plist, then we determined the value of Celsius on it and return that. It’s very easy but it does its job.

But of course, so far this toggle is not useful. If you tap it on SBSettings, you will see it always stays green. That’s because we don’t modify its state anywhere. We are going to implement setState now. Do it like this:

extern "C" void setState(BOOL enabled) {
	NSMutableDictionary *weatherSettings = [NSMutableDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.weather.plist"];
	if(enabled == YES)
	{
		//We want to display weather data in celsius.
		[weatherSettings setObject:[NSNumber numberWithBool:YES] forKey:@"Celsius"];
		NSLog(@"Celsius");
	}else
	{
		//We want to display data in fahrenheit.
		[weatherSettings setObject:[NSNumber numberWithBool:NO] forKey:@"Celsius"];
		NSLog(@"Fahrenheit");
	}
	[weatherSettings writeToFile:@"/var/mobile/Library/Preferences/com.apple.weather.plist" atomically:YES];
}

Also modify getStateFast():

extern "C" BOOL getStateFast() {
	return isEnabled();
}

This is not always a good idea. In this case it’s fast because reading files from disk is not so slow. Depending on your tweak, you may want to implement fast state on a different way.

Finally, we should tell the our toggle that the change state operation will take around 0.6 seconds, o let’s implement that:

extern "C" float getDelayTime() {
	return 0.6f;
}

And that’s it! You may now test it. Launch SBSettings and toggle the tweak. Then launch the weather and see it displaying it’s data in a different system! Then, close the weather app (make sure it’s not suspended in the background), and toggle it again. it will display it’s data as you had it before.

Writing SBSettings Toggles is really easy. I hope this head start helps you understand these little guys better. SBSettings Toggles are actually a big topic, and I hope I revisit them with other tutorials in the near future. Particularly, if there’s enough demand, I may teach you how to run external scripts and functions.

As usual, if you have any questions or problems, let me know. The comment section is open for anyone who needs or wants help. I have changed to Disquss so people don’t hesitate to ask now.

You can grab the project from my Github account here.

(This post can also be found on iOS-Blog: Click here to view it there.)

CHANGELOG:


November 24, 2012 at 7:00 PM [America/La_Paz]: First version of this document.
June 1, 2013 at 5:30 PM [America/La_Paz]: Added a note on correctly installing toggle icons and images.

The post Writing SBSettings Toggles: A Tutorial. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/writing-sbsettings-toggles-tutorial/feed/ 0 119
How To Create MobileSubstrate Tweaks for iOS. https://www.andyibanez.com/create-mobilesubstrate-tweaks-ios/ https://www.andyibanez.com/create-mobilesubstrate-tweaks-ios/#comments Thu, 12 Jul 2012 16:48:21 +0000 http://andyibanez.com/?p=105 Get started with MobileSubstrate Tweaks development with this complete tutorial with a working example.

The post How To Create MobileSubstrate Tweaks for iOS. appeared first on Andy Ibanez.

]]>
This tutorial has code and screenshots for both iOS 6 and under and iOS 7. Credit for adapting the code and the screenshot to the latest iOS version goes to anoadragon453 (anoa), a member of Saurik’s IRC channel.


Welcome to my second tutorial on iOS Open Development! In this tutorial, I will give you the first hand to get started with MobileSubstrate tweaks. If you are here, you should already know what MobileSubstrate is and why you would want to write tweaks for it, so I’m not going to get into too many details about MobileSubstrate and how it works.

Before we get started, there’s a couple of things I want to say first. First, if you don’t know what you are doing (new programmer, first time writing for iOS, whatever), then this is NOT a tutorial for you. MobileSubstrate protects the system from tweak errors and lousy programmers, but one can never be too careful. If you don’t know what you are doing, you could mess up your device, perhaps not bad enough to brick it, but bad enough to require a full restore. So if you follow this tutorial, I trust you know programming, have been doing it for a while, and that you take full responsibility of whatever happens to your device for writing “bad” tweaks.

Second, writing MobileSubstrate tweaks… There’s a lot you can do. It’s all about code hooking thanks to Objective-C’s dynamic nature and there’s a lot you can do with it. I cannot possibly teach you, not even in a thousand posts, everything you can modify with MobileSubstrate tweaks. This small tutorial is just meant to give you a hand getting started with tweaks. It’s not a comprehensive guide. It would really be impossible to write a full book about everything you could hook because there’s too much. Once you are finished with this guide, you will know enough about widgets and you will be able to research on your own: That is, once you are done with this tutorial, you will have to explore the private headers to find what you are looking for and hook it. Writing MobileSubstrate tweaks requires patience and familiarization with the private frameworks, so once you build your first project, start studying the private frameworks: I will tell you how to dump your own headers or where you can find headers that have already been dumped and modified for you to use.

Now, putting that aside…

In this tutorial you are going to write your first MobileSubstrate tweak. We will build a simple tweak that will show the user a message whenever he/she tries to open an app. Once the user taps an icon, we will display a message that says “Launching App…” and then we will launch the app once the user taps an “OK” button. Yes, it is a useless tweak, but you will learn a lot in your way.

In this tutorial, I will do the following:

I will explain you how to get the required headers to create tweaks.
I will explain what “hooking” code is in a nutshell
I will teach you how to hook code using Logos instead of the Objective-C runtime, so you will learn the basic Logos syntax along the way and you will be ready to hook your own things any time.
You will learn three very basic things but they should be powerful enough for you to write any tweak you can think of.

Okay, let’s get started!

First Things First: You Need The Private Headers To Work With.

You need to get the private iOS headers you want to “hook”. Otherwise, well, there’s nothing you can play with. So I will show you ways to do that.

The first and hardest method is to dump your own headers yourself using a command line tool called class-dump-z (or class-dump: refer to this post for more details on the tools). How to use this tools is beyond the scope of this tutorial but a word on them is due. Dumping your own headers is a tedious option. There’s a bunch of private frameworks and it can take a while to dump them all. Save each header to a respective framework directory when done.

Another (and easier) option is to download the headers from someone else. A lot of people have dumped their own headers and are hosting them free of charge on GitHub, so go on there and grab a copy. Personally, I recommend you use rpetrich’s headers, simply because he is a great developer with great reputation in the iOS open development community. But there’s lots of options out there shall you want/need them.

Once you have your headers downloaded, each header nicely inside a folder with the name of the framework, move or copy each framework to the directory called “include” inside the Theos directory. By default, you would save all the headers in the following location (Mac):

/opt/theos/include

Every private framework you download has to be saved there (so you can simple #import them when you want to use them, as you would include any other iOS framework in “official”, non-jailbreak projects).

That is all there is to downloading your headers: Once you are done, you can move on.

But Wait, What Exactly Is “Hooking”?

Objective-C is just a superset of C. Everyone who knows iOS or Mac programming knows that, and it’s the first thing a good book or tutorial on Objective-C tells you. But what exactly does this mean? It simply means that Objective-C has a lot of C code running under the hood. As so, you can freely mix any C code with Objective-C code.

Objective-C is mainly driven by what is called the Objective-C Runtime. The Objective-C Runtime is a C framework that, as you may imagine, controls everything that happens on Ojective-C under the hood. This runtime is responsible of creating classes, creating methods, ivars… Everything Objective-C does, is controlled by this run time. This runtime is in its most superficial level a bunch of C functions, and this framework is the reason Objective-C is such a dynamic language. This runtime can create classes, methods, ivars, and modify any of them along way on runtime rather than on compile time. Here are some functions provided by this framework:

class_addIvar
class_addMethod
objc_msgSend

This are just some functions found in the runtime. Their purpose is really clear: The first one adds an ivar to an existing class. The second one adds a method to an existing class, and the third one sends a message to a class.

Programmers can have full access to the Objective-C Runtime functions by just importing the framework as they would import anything else, granting them access to very powerful tools to modify running code as they wish on runtime and have a lot of control of whatever happens in their machine. In a running application, a developer can “hook” to a current method and make it behave like something completely to what was intended. In other words, to “hook” means to modify or extends the Objective-C code to do what you want. For example, someone could “hook” the NSString’s stringByAppendingString to append the passed parameter and to append a personal string at the end. Suppose I wanted that method to append “Leo” at the end of very string that receives that message. I could hook that method to append the wanted string and my own personal string at the end.

The Objective-C Runtime takes care of hooking. Writing tweaks require hooking, but the iOS Open development community has developed methods to abstract the Objective-C Runtime functions from the programmer. “Hooking” mechanisms have been created precisely to abstract the runtime and perhaps make it less intimidating. CaptainHook is one of them, and Logos is another one. Logos is a very nice hooking mechanism and it abstracts the runtime nicely with simple commands like “%hook” and “%orig”. No idea what I am talking about? In this tutorial I will show you how to create a tweak with Logos, so you will understand more once you see the actual tweak.

Without further ado, time to build the actual tweak and get your hands dirty. This is actually the shorter part of this tutorial but I hope everything I taught you so far will be useful for you in the future.

Getting Our Hands Dirty: Creating The MobileSubstrate Tweak

Alright, launch up the terminal and create a MobileSubstrate project (If this is your first time using Theos, or you don’t even have Theos installed yet, refer to this post. I mainly give links to instructions for installing Theos but the small content I dedicate to installation and configuring should help you getting started with it). I will call mine LaunchNotifier:

Once you have your project created, you will see a file called “Tweak.xm”. This is the file that has all the Logos hooking code and where you write your tweak. So open it up and be surprised for a short while at all that “%” syntax…

You also need to modify the makefile to add the UIKit framework. If you are in this tutorial you should know how to do that, but if you don’t, it’s very simply, just add this line:

LaunchNotifier_FRAMEWORKS = UIKit

And save it. That’s everything we need to do so close it now.

You will see a bunch of commented-out content. Please look at it for a while and try to understand it. It shows you the very basics of code hooking like:

%hook ClassName and %end. This is what I call a “Hook Block”. Everything you write in between is the block where you will be hooking things of the className (note that %hook and %end have no finishing semi-colon in their lines).
%orig calls the original version of the code you are hooking. You will find yourself calling %orig in all your methods most of the time, as they have most of the code needed for the code to be finished. Not calling %orig; can have disastrous consequences in your tweak, depending on what you are doing, although sometimes it’s desirable to avoid calling the original version of the method. Make sure you know when you want to call orig and when you don’t. Usually, it’s just common sense.
%orig(…) is %orig; but with arguments. In the parenthesis you pass in all the parameters to hand them over to the original method.
The rest of the commented code are examples for Logos. They can work, but in this tutorial I want to build something that works, because it’s easier to learn things with “real-world” examples.

Feel free to delete everything on that file for now. We will build everything from scratch (and it’s nothing too big, since it’s just a UIAlertView basically).

First things first, a quick review of what we are doing to do: When the user taps an icon in the SpringBoard, he will see a message telling him he’s about to launch an app.

Okay, so how do we do that? I need to say again, Tweak development requires lots of header browsing. You can never be certain of where things are. You know you want to a show message when an icon is tapped. You know icons are part of SpringBoard, so that’s a good start. You would head over to the SpringBoard framework folder and browse all the headers there. What are you looking for? Common sense is to be used here: You know, it’s an icon, so just look for anything that may have “Icon” on it’s name. What should you hook? You browse your headers for a while and find a class called SBIcon. Is this the one? Could be. Under normal circumstances, you would try to hook different classes and methods, writing code for them to do something (show an UIAlertView is a good idea), to see if you are hooking the right class and method. Okay, nothing interesting here. This can’t be the method. Oh, oh, what do I See? Is that a class called SBApplicationIcon? That sounds less generic and it may be exactly what you are looking for! In our case, yes, we want to hook that class.

You inspect that class and you see it has a method called -launch. Instinctively, we know this method will launch the respective app, so it makes sense to hook it.

The initial hooking will simply look like this:

%hook SBApplicationIcon
//Methods to hook here.
%end

Here we are just saying “I will hook some methods from the SBApplicationIcon class”.

-launch is the method that holds the magic for us, so to hook it, just write it’s implementation inside the hook block like you would anywhere else:

%hook SBApplicationIcon
-(void)launch
{
	//Hooked launch. What will it do now?
}
%end

And there you can just write any Objective-C code you want. We want to show a message, so just write a UIAlertView like this on iOS 6 and under:

%hook SBApplicationIcon
-(void)launch
{
	NSString *appName = [self displayName];
	NSString *message = [NSString stringWithFormat:@"The app %@ has been launched", appName, nil];
	UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:appName message:message delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
	[alert1 show];
	[alert1 release];
	%orig;
}
%end

Or this in iOS 7 and above (you can see we call %orig on this one, passing in the int parameter from the method’s signature:

@interface SBApplicationIcon
- (void)launchFromLocation:(int)arg;
- (id)displayName;
@end

%hook SBApplicationIcon

-(void)launchFromLocation:(int)location
{
	NSLog(@"Hello! I'm LaunchNotifier! Nice to meet you!");
	NSString *appName = [self displayName];
	NSString *message = [NSString stringWithFormat:@"The app %@ has been launched!", appName];
	UIAlertView *myAlert = [[UIAlertView alloc]initWithTitle:appName message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
	[myAlert show];
	[myAlert release];
	%orig(location);
}

%end

(the original -launch method from iOS 6 and under no longer exists on iOS 7)

And that’s really it! make package install your project (you should now that by now). Once the user taps an icon, the app will launch while showing a message:

(iOS 6)

(iOS 7)

A couple of things you have to note about the code we wrote above:

We are calling %orig;. If you don’t call %orig; here, your app won’t launch because you will override all the launch logic. Instead, you will just see your message but nothing will be launched. If a normal user installed this app (without the method calling %orig;), he/she would be screwed. Why? Because he/she wouldn’t be able to launch Cydia and uninstall it. The developer, on the other hand, can reverse the problem by simply fixing the problem and make package installing it again. This is why tweak programming is not for everyone and you should only do it if you plan on doing it well.
Note that we can call [self displayName]. Like I said, you can write any code like you normally code as you usually would, except you can “call hooks” whenever you want or need them.
Congratulations! You have now written your first MobileSubstrate tweak. If you have any questions, head over this post and ask all the questions you may have.

CHANGELOG:


Jan 4, 2014 at 5:03 PM [America/La_Paz]: Updated images. Added code and screenshot for iOS 7.
July 2, 2012 at 12:50 PM [America/La_Paz]: First version of this tutorial.

The post How To Create MobileSubstrate Tweaks for iOS. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/create-mobilesubstrate-tweaks-ios/feed/ 50 105
How To Create A Notification Center Widget for iOS (iOS 6 and Below) https://www.andyibanez.com/create-notification-center-widget-ios-ios-6/ https://www.andyibanez.com/create-notification-center-widget-ios-ios-6/#respond Sun, 12 Feb 2012 20:34:16 +0000 http://andyibanez.com/?p=98 Learn to write notification center widgets with thsi simplified tutorial with a working example.

The post How To Create A Notification Center Widget for iOS (iOS 6 and Below) appeared first on Andy Ibanez.

]]>
Notification Center Widgets.

This tutorial may be different on iOS 7. Please do not try these steps on iOS 7. A tutorial for the latest iOS will be available eventually. Try this in iOS 6 and below only.

In this small tutorial, I’m going to teach you how to write a small notification center widget for iOS using Theos. We’ll be building a simple widget that contains a custom button that, when pushed, it will present a TWTweetComposeViewController modally so you can write tweets from the notification center.

Along the way I will show you how to:

  • Add official iOS frameworks for your Theos projects.
  • How to load images (because with Theos it isn’t as easy as [UIImage imageNamed:]!)
  • How to install other templates for Theos to use.

That’s it. You’ll be building a very simple widget but it should help you getting started with Theos and notification center widgets.

This is not a tutorial for people new to iPhone development, so please make sure you have experience with iOS development before proceeding. There are many websites and books that will teach you iPhone development, so if you don’t know anything about iOS development, spend a few months building (or attempting to build) an app or two first: I’ll wait for you and this tutorial won’t be going anywhere.

Finally, I’ not going to teach you how to install Theos on your computer. Please refer to this page to learn how to install and configure Theos properly for your machine. The good thing about Theos is that you don’t need a Mac to write your widgets, you can use Linux and Windows as well. Once you have everything, we can get started!

1. Getting The Templates

First things first: Theos doesn’t come with the needed template to write Notification Center widgets, so you have to download that first.

Dustin L. Howett (@DHowett), the creator of Theos, is hosting the Notification Center widget and a few other templates on big Github here. Feel free to download all of them, but we only need “notification_center_widget.nic” for this tutorial.

Once you download the template (shouldn’t take long since it is a small file), save it on the YOUR_THEOS_DIRECTORY/templates/iphone/ directory (On Mac OS X, the default location is /opt/theos/templates/iphone/).

Once you have that, you don’t need to do anything else.

2. Creating The Notification Center Widget Project

Time to create the actual project. Open up the Terminal Window/Windows command prompt and navigate to a directory of your choice. I recommend you you create the project in a folder under your account’s folder to avoid permission issues. Another recommendation is to avoid having folders with spaces in their names. At least to me under Mac OS X, Theos had problems creating and compiling the source when the project path had spaces or other special characters.

Once you navigate to the folder of your choice on your terminal window, type in this command (it will vary according to your OS, but it’s just the directory of the nic.pl file):

/opt/theos/bin/nic.pl

That will show you the list of available templates for Theos to use (the option should 5, so go ahead and press that).

It will ask you for a project name. Something like “iNotitweet” or anything you want will do.

Next it will ask you for a package name, which is the reverse-DNS notation you used on normal iPhone projects before. I named my package “com.leonnears.openDev.inotitweet”.

Author/Maintainer name is self explanatory.

If for some reason you got lost in the wizard, here you have a screenshot of the terminal window once it is complete. It shows all the commands you need to put in order to create your project:

3. Building the Actual Widget.

Okay, time to get started with the juice!

Go to your project’s folder. Theos has created a project folder with the project name (with no caps) in the folder you were in when you executed the commands.

In your folder, you will have the following files and folders:

  • BBWeeAppController-Protocol.h: This is the protocol needed by the widget. You won’t need to modify this file 99% of the time.
  • control: This is an interesting file. It contains all the metadata about your project such as iOS Firmware requirements, the author, project name, identifier. Additionally, in this file you can define it’s Cydia details, such as the tweak’s description, what folder it should be located in, etc.. In the end you will need to modify this file a bit, at least to change it’s description.
  • {YOUR_PROJECT_NAME}Controller.m (In my case, and in your case if you named the project iNotitweet, iNotitweetController.m): This is the main of your whole project. Here you will write absolutely all the code needed for your widget.
  • Makefile: Another very important file. In this file you tell your widget which iOS frameworks it needs and so on. You may or may not need to modify this file, depending on your project (in our case, we will modify this file).
  • Resources Folder: Here you need to save all the resources for your project, including but not limited to images.
  • Theos (shortcut): This is just a shortcut to the Theos directory in your PC.

So let’s start. Open up your favorite code editor, and open iNotitweetController.m. Time to get our hands dirty.

The first thing you will notice is that there is no companion iNotitweetController.h file for the file you have open. This is because for tweak development, it is a lot better to both define and implement classes in the same file. It’s a lot better to compile and deploy them that way.

Look for the implementation of loadPlaceHolderView, and you will find this. Don’t erase or modify this code unless you need to! This code will load the placeHolder view where your widget goes. Unless you want more customization or want to change the widget’s size, you don’t need to modify the coded provided to you here. Of course we can add more code below it, but the rest of the work is done in other methods of the template (such as loadFullView).

Go get some buttons for the project and save them in your project’s Resources Folder. You will need both a retina and a low resolution button. Well since this is not an official project, you can just use one or the other. If you don’t want to look for buttons, I have made this for an imomplete widget I was writing. Feel free to use this buttons for this test project only:

Now it’s time to start writing some code, and keep in mind one important thing. You need to write all your UI objects by hand. You cannot use Interface Builder for your widgets or other Theos template projects. So make sure you know how to build objects with code (if you can’t, you can pick this up along the way since it’s not a hard thing to do).

In the templates, you can code as you would with any other iOS projects. The process of creating is exactly the same. Only deployment is different compared to official iOS development.

Go to the loadFullView method. Here we create the objects to the views created by loadPlaceholderView.

Before proceeding, you will need to add a button, but like I said at the beginning of this tutorial, it’s not as easy as [IImage imageNamed:]. When it comes to tweak development, using imageNamed would look for an image on your device’s Springboard, which won’t work 99% of the time (unless you guess the name of a springboard image!).

At the beginning of your code there is a method called +(void)initialize. The only code here is the creation of an NSBundle that looks like this:

_iQuickCommunicatorWeeAppBundle = [[NSBundle bundleForClass:[self class]] retain];

You will need to use this object to fetch all the resources in your Resources folder. It may look obvious, but the truth is that at first when I started making widgets, I literally spent around 9 hours trying to figure out how to load images, because none of the methods worked. Luckily for me, DHowett came to my rescue and explained to me how things work, and now I’ explaining this to you to save a wasteful afternoon or even whole day!

Now that you know that and have those tweet buttons saved in your Resources folder, we may proceed.

We are going to create the UI first an later we will concern ourselves with the actual functioning of the tweak. So create a single custom UIButton, and, based on the explanation above, set the tweet buttons as the button’s background. Then just add it as a subview of _view:

This is what I did:

—————————-

-(void)loadFullView {
 // Add subviews to _backgroundView (or _view) here.
 UIButton *tweet = [UIButton buttonWithType:UIButtonTypeCustom];
 ] forState:UIControlStateNormal];
 tweet.frame = CGRectMake(2, 0, 79, 33);
 ;
 [_view addSubview:tweet];
}

—————————-

You may have noticed the selector doesn’t exist. So just scroll all the way up and add it to the class definition at first as -(void)composeTweet. Then go back down to the class implementation and write it’s skeleton like this:

-(void)composeTweet {

}

Finally, we will actually modify our widget’s size to be as tall as the button, so go down to the loadPlaceholderView, and look for this line:

_view = [[UIView alloc] initWithFrame:(CGRect){CGPointZero, {316.f, [self viewHeight]}}];

This line should be the first one in the method. Do the following small change:

_view = [[UIView alloc] initWithFrame:(CGRect){CGPointZero, {316.f, 33.f}}];

The button is 33 points high, so our widget should be just as big.

Time to compile and see what we got! We are not finished yet, but so far it’s a great point to see if what we have looks fine. So in your terminal window type the following commands:

export THEOS_DEVICE_IP=INSERT_YOUR_DEVICES_IP_HERE
make package install

Theos will create a deb and install it over sftp to your device. So make sure your computer where you are developing and your phone are connected to the same network before running those commands.

Once it compiles, Theos will put it in your device and your device will respiring afterwards. Now you need to enable your widget in the Notification Center settings before you can see it, so go to Settings > Notifications > scroll down till you find iNotitweet (or the name of your project), dig in and enable it.

Once you enable it, if you have been following along, you should see the widget in the notification center like this:

We’re almost finishing. Everything that is left is to actually show the TWTweetComposeViewController view when the button is tapped. And that is easy as pie.

First, you need to include the Twitter framework at the beginning of your code:

#import <Twitter/Twitter>

But that alone won’t link your project to the Twitter framework. Now open up the Makefile file and look for this line.

iNotitweet_FRAMEWORKS = UIKit CoreGraphics

Like you can see, linking to frameworks is pretty straightforward. You just add an space and the name of the framework you are linking against. Since we want to link Twitter, everything you have to do is:

iNotitweet_FRAMEWORKS = UIKit CoreGraphics Twitter

And that’s it! You can now save the file and forget about it for the rest of the tutorial.

The final step is to implement composetTweet. For that, you have to create the TWTweetComposeViewController first and present it modally. Now this is a bit tricky and you may be asking yourself how can we present it modally if our widget is a subclass of NSObject rather than of an object that implements presentModalViewController:animated? I actually scratched my head against this problem before but I finally found the solution. The trick is to create a normal UIViewController object below the tweet composer, and set this UIViewController’s view property to your widget’s _view property. After you do that, just configure the tweet composer to dismiss when cancel is pushed and the rest of the configurations. Configuring the tweet compose view is out of the scope of this tutorial, so I will just show you the code (it should be pretty easy to understand):

-(void)composeTweet {
 TWTweetComposeViewController *twtComposer = [[TWTweetComposeViewController alloc] init];
 UIViewController *someVc = [[UIViewController alloc] init];
 twtComposer.completionHandler = ^(TWTweetComposeViewControllerResult result)
 {
 if(result == TWTweetComposeViewControllerResultCancelled)
 {
 [someVc dismissModalViewControllerAnimated:YES];
 }
 };
 someVc.view = _view;
 [someVc presentModalViewController:twtComposer animated:YES];
 [someVc release];
 [twtComposer release];
}

And that’s it! make package install it into your phone and push the button. The tweet composer will show up and you can tweet from it, or you can dismiss the window in case you change your mind.

That’s it folks! If you were/are having problems following the tutorial, I have put the full iNotitweetController.m file on my Pastebin, so feel free to download the code to compare it with yours, or to play with it in order to understand it more. Additionally, you may follow me on Twitter (@AndyIbanezK) or leave your questions in the comments.

Oh, one more thing before I forget, to uninstall the widget, go to Cydia and look for your widget in the installed packages. You can uninstall it from there.

I will appreciate any feedback. This is the second tutorial I have ever written.

CHANGELOG:


January 4, 2014 at 4:33 PM [America/La_Paz]: Added note regarding iOS 7. Changed Twitter username where mentioned.
February 20, 2012 at 4:30 PM [America/La_Paz]: First version of this post.

The post How To Create A Notification Center Widget for iOS (iOS 6 and Below) appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/create-notification-center-widget-ios-ios-6/feed/ 0 98