Talk:Wikimedia Apps/Team/iOS/BestPractices

From mediawiki.org

I hope to add a section here on how to improve testability w/ DI and class extensions. BGerstle (WMF) (talk)

UI code responsibilities[edit]

Expected behaviors/contracts[edit]

Views (and related types, like layout implementations) have a few implicit—but expected—behaviors that are usually derived from conventions set by Apple in UIKit or other widely-used open source libraries. The more important and commonly-encountered ones are listed below to help engineers make design decisions about the views they create or modify:

On DRYness and reinventing of wheels[edit]

When at all possible, leverage existing APIs (e.g. UIKit) to implement the desired effect, such as alpha, CALayer properties, etc. There are many reasons for this:

  1. Duplicating state with another property requires extra code (read: maintenance cost) to manually maintain consistent state the new property and its "native" counterparts
  2. Using existing APIs makes code easier to compose and reuse (e.g. code that manipulates frames, transforms, etc. can be composed to create a new "higher order" functionality)
  3. Leveraging existing APIs allows your component to hook into other abstractions provided by UIKit (e.g. the +[UIView animateWithDuration:...] API)

No side effects[edit]

Unless made explicit, changes to one property should not effect others. For example, if I hide a view, then show it again, it should have the same appearance when redisplayed. Here's an example of what not to do with a custom UIView:

  1. As a user, configure the view with a specific alpha so it has a desired appearance
  2. Hide and show the view (animated or not)
  3. When hidden/shown, the custom view's implementation changes its alpha, thereby overriding my configuration and breaking the "no side effects" contract

Writable properties[edit]

Writable properties on a UI type should be idempotent, lazy, and reactive. Typically this is done by overriding the setter of your new property, checking for a change, and flagging it for later update:

- (void)setProperty:(id)value {
  if ([_value isEqual:value]) {
    // idempotent, only causes an update if the value is actually different
    return;
  }
  _value = value;
  // be reactive, but lazy by using the "setNeedsXXXX" methods, and let the caller decide if 
  // updates should be strict (by making a subsequent call to xxxxIfNeeded) or lazy (updated at the next runloop iteration)
  [self setNeedsLayout]; // or setNeedsDisplay or invalidateLayout if using autolayout
}
Animatable properties[edit]

As mentioned above, this can sometimes be functionality your view gets "for free" by using APIs that are themselves animatable using the +[UIView animateWithDuration:....] API. In this case, there's no need to provide anything other than a settable property that can be used within an animation block. However, in cases where you have custom drawing code related to certain properties, you'll often need to use lower-level APIs to make them animatable and provide a suitable high-level abstraction in order to explicitly animate them. objc.io has a great article covering how to implement animations for custom properties using CoreAnimation.[1]