Dev:Cydia Substrate Pitfalls

Follow the following guidelines to avoid common MobileSubstrate mistakes:


 * 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.
 * 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".
 * 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 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.
 * 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.)
 * Use autoresizingMask and layoutSubviews to make your extension resolution-independent and rotation-aware.
 * Don't modify OS or Cydia files; use runtime-replacement instead.
 * 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.
 * 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.)
 * If your extension exposes an API, document it!
 * Include headers inside your package so they're easy to find.
 * Ensure your extension is localizable; use the standard .lproj system where possible. Avoid having images that include text.
 * Don't make assumptions about the ivar layout of a class; these can and will change across firmware versions.
 * 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.
 * 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.)
 * 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.
 * 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.
 * 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.
 * 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.)
 * 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.
 * 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.)
 * 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.
 * 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.
 * Use MSHookMessageEx instead of MSHookMessage.
 * MSHookMessage allows you to specify an optional prefix which gets added to the original method, polluting the class.