Cydia Substrate Pitfalls
From iPhone Development Wiki
(Redirected from MobileSubstrate Pitfalls)
- Do not use UIDevice in your constructor if you are hooking SpringBoard!
- Access ivars through their associated properties, where practical.
- Always call the old implementation of a method unless you require suppressing the default behaviour. Similarly, only call the old implementation of a method from within the method hook.
- Use documented/public SDK features wherever practical. Apple usually tests private APIs internally before making them public; often the public version is the same or similar to the private version that was in previous OS releases.
- Avoid UIAlertView in SpringBoard--it doesn't participate in the SpringBoard-wide alert system; use a custom SBAlertItem subclass instead (not necessary for cases where you don't care if the system destroys your alert.)
- Use autoresizingMask and layoutSubviews to make your extension resolution-independent and rotation-aware.
- Avoid doing extra work--the methods your extension hooks may be called from inside a tight loop; disk access is considered a LOT of extra work (related: properties are actually method calls, they can be slow.)
- UI* methods must be performed on the main thread (this includes UIImage! use CGImageSource and company if you need to load images from a background thread.)
- Never block the main thread! Performing any operation that can take significant time on the main thread is evil (that includes any operation that hits the network.)
- Access resources through NSBundle so that they may be themed via WinterBoard.
- Don't make assumptions about the ivar layout of a class; these can and will change across firmware versions.
- If accessing low-level graphics hardware, beware that first-generation devices do _not_ have a unified memory model (VRAM and regular RAM are separate regions.)
- Participate in the memory warning system if your extension has a significant memory footprint; release everything you can.
- Use mmap when working with large datasets--the kernel will swap it in and out of memory as necessary (but avoid heavy writes, as that's no better than virtual memory.)
- Compile against the earliest possible firmware you can (compile against 3.0, not 3.0.1, 3.1 or 3.1.2); use feature detection if necessary.
- Strip symbols if you have something to hide; use standard C (or C++) for the hidden parts--Objective-C will spill your details.
- Many private frameworks have public headers available for Mac OS X; use them (also, there are header packs floating around--kennytm's is one of the best and is legal to distribute.)
- Only link to the frameworks you actually need--importing unnecessary frameworks will increase app launch time (use dlopen/dlsym to load libraries and access symbols at runtime after the app has launched)
- This is less of a concern on 3.1+, due to the dyld shared object cache. You still incur the overhead of library initializers, however.
- Use a private prefix on classes and category methods: Objective-C has NO namespacing capabilities. (examples: two classes called Preferences that behave differently; two extensions define a -[NSData base64String] that behave slightly differently.)
- Avoid waking the device or keeping the device from sleeping. If your extension needs to do something periodically, hook the Mail application's sync and integrate the action within that (scheduler/alarm extensions need not heed this warning.)
- Avoid CPU-/GPU-/disk-intensive activity where the user would not expect it (battery life!)
- Don't overuse NSLog/CFLog/fprintf(stderr, …); they're slow and synchronous.
- If your extension exposes an icon, include both the 29x29 and 59x60 icon sizes (more for iPad.)
- Avoid changing the class of objects returned by public APIs--some App Store applications perform unnecessary checks against the class of an object and will fail.
- Prefer binary plists over XML--they're much quicker (OpenSTEP-style text plists are also quick, but seem to be deprecated)
- Manage memory correctly as per the Cocoa memory management guidelines; ensure all hooks comply as well.
- Respect the user's privacy.
- The IMEI, telephone number, contacts list and email inbox are definitely private information.
- The MAC address, Bluetooth address and UDID may be considered private information, depending on the jurisdiction.
- Store preferences in /var/mobile/Library/Preferences to have iTunes back it up.
- Store data in /var/mobile/Library/AddressBook or /var/mobile/Library/Keyboard to avoid the sandbox restrictions, if necessary.
MobileSubstrate APIs and states
- Always include a MobileLoader filter plist.
- If you require access to only SpringBoard, use "com.apple.springboard"; if you require access to all UIKit apps, use "com.apple.UIKit".
- If you hook a framework like "com.apple.UIKit", test your tweak in non-UIKit processes anyway (e.g. by temporarily changing the filter to "com.apple.Security" or "com.apple.CoreFoundation"). Broken tweaks with incorrect/missing filters will pull your tweak into processes you don't expect. However, there is a way to prevent unwanted code injection to non-UIKit processes. Modify if needed. See this.
- Don't modify OS or Cydia files; use runtime-replacement instead.
- Preferences.framework changed drastically between 3.1 and 3.2. Use PreferenceLoader and standard preferences plists for simple panes, PSViewController + UITableView for more complex panes (PSListController is okay for simple preferences, but is less subject to OS changes; PSViewController is more powerful, but many implementations were broken between 3.1 and 3.2).
- If your extension exposes an API, document it!
- Include headers inside your package so they're easy to find.
- Use an established hooking framework such as substrate.h, CaptainHook.h or Logos; there is little benefit in rolling your own, and too many details have to be just right.
- Never inhibit MobileSubstrate's safe mode.
- If your extension manipulates icon layouts, use IconSupport so you don't stomp all over other extensions' modifications or the stock/safe-mode layout.
- Expect others to hook the same methods you do; expect them not to have read this list and to have done crazy things.
- Prefer hooking methods over hooking functions--new jailbreaks don't always have kernel patches (although this is not as much of a concern, as jailbreakers understand the need for W^X) and MobileSubstrate may not support newer ARM instructions.
- Use MSHookMessageEx instead of MSHookMessage.
- MSHookMessage allows you to specify an optional prefix which gets added to the original method, polluting the class.
- Include the proper Depends: line in your Debian control file (that way Cydia will automatically install dependencies and will alert users when they are missing required components like firmware version.)
- Ensure your extension is localizable; use the standard .lproj system where possible. Avoid having images that include text.