Difference between revisions of "Cydia Substrate Pitfalls"

From iPhone Development Wiki
Jump to: navigation, search
m (Coding practices: Update icon ratio for post 2009 devices)
 
(15 intermediate revisions by 4 users not shown)
Line 1: Line 1:
Follow the following guidelines to avoid common MobileSubstrate mistakes:
+
Follow the following guidelines to avoid common [[MobileSubstrate]] mistakes. See also: [[Best Practices]].
  
# Access ivars through their associated property if it exists
+
== Coding practices ==
# Always call the old implementation of the 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 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 they system destroys your alert)
 
# Use documented/public SDK features where possible.  Apple usually tests private APIs internally before making them a public API; 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; buyer beware. Use PreferenceLoader and standard preferences plists for simple panes, PSViewController+UITableView for more complex panes (using PSListController is more difficult than it should be and is subject to many OS changes)
 
# 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 that way they can 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 deb 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.
 
# Make sure your deb package doesn't include hidden .DS_Store, ._*, or thumbs.db files
 
# Don't assume the ivar layout of classes; they can and will change
 
# Use an established hooking framework (substrate.h, CaptainHook.h or logos); there are too many details that 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 (commonly, this occurs with invalid icon layouts--IconSupport to the rescue)
 
# 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 users' privacy. The IMEI, cell number, contacts list and email inbox are definitely private information. 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)
 
# Use a private prefix on classes and extension methods: Objective-C has NO namespacing capabilities. (examples: two Preferences classes 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
 
# Avoid waking the device or keeping the device from sleeping. If your extension needs to do something periodically, hook the Mail applications's sync and integrate the action within that. (scheduler/alarm extensions need not apply)
 
# 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 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 and don't force an XML parser to load (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. MSHookMessageEx is thread-safe, whereas MSHookMessage isn't
 
  
This list is a work in progress. Please edit as necessary --rpetrich
+
* Never use <code>[[UIDevice]]</code> 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 behavior. 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 <code>[[UIAlertView]]</code> in SpringBoard--it doesn't participate in the SpringBoard-wide alert system; use a custom <code>SBAlertItem</code> subclass instead (not necessary for cases where you don't care if the system destroys your alert.)
 +
* Use <code>-autoresizingMask</code> and <code>-layoutSubviews</code> to make your extension resolution-independent and rotation-aware. However, if there are any modern APIs from Apple doing the same, consider using that. <code>-layoutSubviews</code> can be invoked too many times and the performance may be reduced.
 +
* 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.)
 +
* <code>UI*</code> methods must be performed on the main thread (this includes <code>[[UIImage]]</code>! use <code>CGImageSource</code> 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 <code>NSBundle</code> 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 <code>mmap()</code> 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_cache|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 <code>-[NSData base64String]</code> 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 <code>NSLog(), CFLog(), fprintf(stderr, …);</code> they're slow and synchronous.
 +
* If your extension exposes an icon, include both the 29x29, 59x60, and 90x90 icon sizes. 60x and 90x files can be suffixed with <code>@2x</code> or <code>@3x</code> (e.g. <code>image@2x.png</code>), and iOS will handle loading images for higher pixel ratios. Devices newer than the 3GS typically have a ratio of at least 2x, so these should typically be included.
 +
* 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.
 +
* Be aware that <code>-[NSBundle bundleIdentifier]</code> may return <code>nil</code> if it's invoked within an arbitrary process that does not belong to any specific bundle. When it's used with certain other methods that expect the non-null value, it can indefinitely crash the process. In some cases, it will drain the battery because the process will keep restarting. This is some [https://github.com/pr0crustes/SmoothTable/issues/2 bad example].
 +
* 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 [[MobileSubstrate#MobileLoader|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. Even the recent versions of Cydia Substrate reject the tweaks without the filter file, tweaks targeting UIKit can still run in such processes you don't expect. However, there is a way to prevent unwanted code injection to non-UIKit processes. See [https://ghostbin.com/paste/kr9ob this]. For more filtering options (for example, injection into keyboard extensions), see [https://github.com/PoomSmart/PSHeader/blob/master/PS.h here].
 +
* 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 [[MobileSubstrate#Safe_mode|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 [http://www.cydiasubstrate.com/api/c/MSHookMessageEx/ MSHookMessageEx] instead of [http://www.cydiasubstrate.com/api/c/MSHookMessage/ 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.

Latest revision as of 18:22, 14 March 2020

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

Coding practices

  • Never 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 behavior. 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. However, if there are any modern APIs from Apple doing the same, consider using that. -layoutSubviews can be invoked too many times and the performance may be reduced.
  • 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, 59x60, and 90x90 icon sizes. 60x and 90x files can be suffixed with @2x or @3x (e.g. image@2x.png), and iOS will handle loading images for higher pixel ratios. Devices newer than the 3GS typically have a ratio of at least 2x, so these should typically be included.
  • 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.
  • Be aware that -[NSBundle bundleIdentifier] may return nil if it's invoked within an arbitrary process that does not belong to any specific bundle. When it's used with certain other methods that expect the non-null value, it can indefinitely crash the process. In some cases, it will drain the battery because the process will keep restarting. This is some bad example.
  • 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. Even the recent versions of Cydia Substrate reject the tweaks without the filter file, tweaks targeting UIKit can still run in such processes you don't expect. However, there is a way to prevent unwanted code injection to non-UIKit processes. See this. For more filtering options (for example, injection into keyboard extensions), see here.
  • 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.