Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Everyone is an API designer

Everyone is an API designer

The slides from my talk "Everyone is an API designer", presented at NSSpain and Mobiconf 2017. The first 3/4 of the slides are used in both talks, while the last 25% were only in the extended director's cut HD remaster version presented at Mobiconf.

You can find a recording of the NSSpain version of this talk here: https://vimeo.com/234961067

John Sundell

October 05, 2017
Tweet

Transcript

  1. class ProfileViewController: UIViewController { var headerView: ProfileHeaderView? var nameLabel: UILabel?

    var logOutButton: UIButton? } override func loadView() { super.loadView() let headerView = ProfileHeaderView() headerView.delegate = self self.headerView = headerView }
  2. override func loadView() { super.loadView() let headerView = ProfileHeaderView() headerView.delegate

    = self self.headerView = headerView } func userDidUnlockPremiumSubscription() { profileController.headerView = PremiumHeaderView() }
  3. class ProfileViewController: UIViewController { } var headerView: ProfileHeaderView? var nameLabel:

    UILabel? var logOutButton: UIButton? private private private extension ProfileViewController { enum Theme { case standard case premium } func style(with theme: Theme) { headerView?.apply(theme) } }
  4. class ListViewController: UIViewController { init(coreDataContext: NSManagedObjectContext) { ... } }

    class DetailViewController: UIViewController { init(coreDataContext: NSManagedObjectContext) { ... } } class ProfileViewController: UIViewController { init(coreDataContext: NSManagedObjectContext) { ... } }
  5. protocol Database { func loadObjects<T: Model>(matching query: Query) -> [T]

    func loadObject(withID id: String) -> T? func save<T: Model>(_ object: T) }
  6. class DatabaseMock: Database { ... } protocol Database { func

    loadObjects<T: Model>(matching query: Query) -> [T] func loadObject(withID id: String) -> T? func save<T: Model>(_ object: T) } extension NSManagedObjectContext: Database { ... } extension Realm: Database { ... }
  7. Great APIs are contained… …they don’t expose their implementation details…

    …which removes confusion and reduces the risk of “invalid use”. Protocols are a great way to define contained APIs… …that can have their underlying implementation easily changed.
  8. struct CATransform3D { init(m11: CGFloat, m12: CGFloat, m13: CGFloat, m14:

    CGFloat, m21: CGFloat, m22: CGFloat, m23: CGFloat, m24: CGFloat, m31: CGFloat, m32: CGFloat, m33: CGFloat, m34: CGFloat, m41: CGFloat, m42: CGFloat, m43: CGFloat, m44: CGFloat) }
  9. actionSheet.addButton(button: finishButton, dismiss: true) Button, button, button $ What is

    this dismissing? % class ActionSheetViewController: UIViewController { func addButton(button: UIButton, dismiss: Bool) { ... } }
  10. Great APIs are conversational… …they make their intent clear by

    using readable declarations… …which eliminates cruft and makes usages more obvious. By defining separate APIs for separate actions & functionality… …we can make them easier to read, and harder to misunderstand.
  11. extension User { func serialize() -> [String : Any] {

    return [ "name": name, "age": age, "hobbies": hobbies.map { $0.serialize() }, "address": address, "phoneNumber": phoneNumber ?? NSNull() ] } }
  12. func wrap<T>( _ object: T, keyConverter: KeyConverter, propertyOverrides: [String :

    Any], propertyNamesToIgnore: Set<String> ) throws -> Data
  13. func wrap( context: Any?, dateFormatter: DateFormatter? ) -> Any? }

    var wrapKeyStyle: WrapKeyStyle { get } func keyForWrapping( propertyNamed propertyName: String ) -> String? protocol WrapCustomizable {
  14. func wrap( context: Any?, dateFormatter: DateFormatter? ) -> Any? }

    var wrapKeyStyle: WrapKeyStyle { get } func keyForWrapping( propertyNamed propertyName: String ) -> String? protocol WrapCustomizable {
  15. public extension WrapCustomizable { } var wrapKeyStyle: WrapKeyStyle { return

    .matchPropertyName } func wrap( context: Any?, dateFormatter: DateFormatter? ) -> Any? { ... } func keyForWrapping( propertyNamed propertyName: String ) -> String? { ... }
  16. Great APIs are scalable… …they are easy to start using…

    …yet flexible enough for more advanced use cases. This can be achieved using extensions & default arguments… …and by moving overrides from the call site to type definitions.
  17. struct Animation { var duration: TimeInterval var curve: UIViewAnimationCurve var

    completionHandler: () -> Void } class ProfileViewController: UIViewController { init(profile: Profile, showFriends: Bool) } func present(_ profileViewController: ProfileViewController, with animation: Animation? = nil) { ... }
  18. func present(_ profileViewController: ProfileViewController, with animation: Animation? = nil) {

    ... } func presentProfile(animated: Bool, duration: TimeInterval, curve: UIViewAnimationCurve, showFriends: Bool, completionHandler: @escaping () -> Void) { @available(*, deprecated, renamed: “present(with:)") } ...
  19. duration: TimeInterval, curve: UIViewAnimationCurve, showFriends: Bool, completionHandler: @escaping () ->

    Void) { } let viewController = ProfileViewController(profile: profile, showFriends: showFriends) let animation: Animation? if animated { animation = Animation(duration: duration, curve: curve, completionHandler: completionHandler) } else { animation = nil } present(viewController, with: animation)
  20. Great APIs are constantly evolving… …rather than just growing in

    scope and complexity… …they are refactored and broken up into new ones. Backwards compatibility is a really beautiful thing… …and can be easily achieved using deprecations or additive changes.