Cydia Substrate Pitfalls

From iPhone Development Wiki
(Redirected from MobileSubstrate Pitfalls)
Jump to: navigation, search

Follow the following guidelines to avoid common MobileSubstrate mistakes. See also: Best Practices.

Coding practices

  • 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.

Data management

  • 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.

Packaging

  • 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.