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

Refactoring iOS Projects - Ukraine

Refactoring iOS Projects - Ukraine

For many developers, refactoring is a menu in their IDE. The truth is that there are tons of refactoring techniques, most of them described in detail by Martin Fowler in his book of that name. In this session we are going to learn simple yet effective techniques to refactor large iOS codebases in order to make them more testable, to adapt them to be eventually rewritten in Swift, and to make them as "future proof" as possible.

Presentation given in Dnipropetrovsk, Ukraine, on July 16th, 2016, at https://uamobi.tech

Speech: https://akos.ma/blog/refactoring-ios-projects/

Adrian Kosmaczewski

July 16, 2016
Tweet

More Decks by Adrian Kosmaczewski

Other Decks in Technology

Transcript

  1. Рефакторинг ! Проектів
    Refactoring ! Projects
    @akosma – @uaMobiTech – July 16th 2016
    © akosma 2016 1

    View full-size slide

  2. Рефакторинг !⌚#$ Проектів
    Refactoring !⌚#$ Projects
    @akosma – @uaMobiTech – July 16th 2016
    © akosma 2016 2

    View full-size slide

  3. © akosma 2016 3

    View full-size slide

  4. © akosma 2016 4

    View full-size slide

  5. Thank you !
    More information at developer.apple.com
    © akosma 2016 5

    View full-size slide

  6. © akosma 2016 6

    View full-size slide

  7. Sorry! !
    © akosma 2016 7

    View full-size slide

  8. © akosma 2016 8

    View full-size slide

  9. © akosma 2016 9

    View full-size slide

  10. Thank you !
    More information at jetbrains.com/objc
    © akosma 2016 10

    View full-size slide

  11. © akosma 2016 11

    View full-size slide

  12. Seriously? !
    © akosma 2016 12

    View full-size slide

  13. Live and Let IDE !
    © akosma 2016 13

    View full-size slide

  14. Big Topic !
    © akosma 2016 14

    View full-size slide

  15. © akosma 2016 15

    View full-size slide

  16. More !
    © akosma 2016 16

    View full-size slide

  17. © akosma 2016 17

    View full-size slide

  18. Yeah baby !"
    © akosma 2016 18

    View full-size slide

  19. © akosma 2016 19

    View full-size slide

  20. What is it? !
    © akosma 2016 20

    View full-size slide

  21. Refactoring (noun):
    a change made to the internal
    structure of software to make
    it easier to understand and
    cheaper to modify without
    changing its observable
    behavior.
    © akosma 2016 21

    View full-size slide

  22. Why to? !
    © akosma 2016 22

    View full-size slide

  23. —To improve the design of software
    —To make it easier to understand
    —To make it easier to find and fix bugs
    —To program faster
    —To annoy your manager
    © akosma 2016 23

    View full-size slide

  24. When to? ⏰
    © akosma 2016 24

    View full-size slide

  25. —When you add features
    —When fixing bugs
    —During code reviews
    © akosma 2016 25

    View full-size slide

  26. When not to? ⛔
    © akosma 2016 26

    View full-size slide

  27. © akosma 2016 27

    View full-size slide

  28. 1. Add Tests
    2. Refactor
    © akosma 2016 28

    View full-size slide

  29. © akosma 2016 29

    View full-size slide

  30. © akosma 2016 30

    View full-size slide

  31. How to? !
    © akosma 2016 31

    View full-size slide

  32. Bad Smells !68
    —Duplicated code
    —Long methods
    —Large class
    —Long parameter list
    —Divergent change
    —Shotgun surgery
    —Feature envy
    —Data clumps
    —Primitive obsession
    —Switch statements
    —Parallel inheritance hierarchies
    —Lazy class
    —Speculative generality
    —Temporary field
    —Message chains
    —Middle Man
    —Inappropriate intimacy
    —Alternative classes with different interfaces
    —Incomplete library class
    —Data class
    —Refused bequest
    —Comments
    68 Source: “Refactoring” by Martin Fowler (1999)
    © akosma 2016 32

    View full-size slide

  33. !⌚#$ Specific Smells
    ! Swift and Cocoa Smells
    ! Class Design Smells
    ! Project Management Smells
    © akosma 2016 33

    View full-size slide

  34. ! Swift and Cocoa Smells
    © akosma 2016 34

    View full-size slide

  35. !" SwiftyLeaks
    © akosma 2016 35

    View full-size slide

  36. © akosma 2016 36

    View full-size slide

  37. Memory Leaks in Swift
    Reference Cycles23:
    —Instances
    —Closures
    23 Source: Apple Developer Library
    © akosma 2016 37

    View full-size slide

  38. Instance Reference Cycles (1/2)
    class Person {
    let name: String
    var apartment: Apartment?
    }
    class Apartment {
    let unit: String
    var tenant: Person? // Ouch!
    }
    © akosma 2016 38

    View full-size slide

  39. © akosma 2016 39

    View full-size slide

  40. Instance Reference Cycles (2/2)
    class Person {
    let name: String
    var apartment: Apartment?
    }
    class Apartment {
    let unit: String
    weak var tenant: Person? // Yay!
    }
    © akosma 2016 40

    View full-size slide

  41. © akosma 2016 41

    View full-size slide

  42. weak vs. unowned
    —weak can be nil
    —unowned cannot be nil
    —unowned ➡ always nonoptional types
    © akosma 2016 42

    View full-size slide

  43. Closure Reference Cycles (1/2)
    let obj = SomeObject()
    obj.blockMember = {
    return obj.property // Ouch!
    }
    © akosma 2016 43

    View full-size slide

  44. Closure Reference Cycles (2/2)
    let obj = SomeObject()
    obj.blockMember = { [unowned obj]
    return obj.property // Yay!
    }
    © akosma 2016 44

    View full-size slide

  45. Closures are reference types⾠
    © akosma 2016 45

    View full-size slide

  46. !" Hungarian Notation
    © akosma 2016 46

    View full-size slide

  47. protocol IUnknown {
    func QueryInterface (_ riid: REFIID, _ ppvObject: LPVOID) -> HRESULT
    func AddRef() -> ULONG
    func Release() -> ULONG
    }
    protocol IDispatch : IUnknown {
    func GetTypeInfoCount(pctinfo: UInt) -> HRESULT
    func GetTypeInfo(_ iTInfo: UInt, _ lcid: LCID,
    _ ppTInfo: ITypeInfo) -> HRESULT
    func GetIDsOfNames(_ riid: REFIID, _ rgszNames: OLECHAR,
    _ cNames: UInt, _ lcid: LCID,
    _ rgDispId: DISPID) -> HRESULT
    func Invoke(_ dispIdMember: DISPID, _ riid: REFIID, _ lcid: LCID,
    _ wFlags: WORD, _ pDispParams: DISPPARAMS,
    _ pVarResult: VARIANT, _ pExcepInfo: EXCEPINFO,
    _ puArgErr: UInt32) -> HRESULT
    }
    © akosma 2016 47

    View full-size slide

  48. let oPerson = Server.CreateObject("Person")
    var bBusy = true
    if (bBusy) {
    bBusy = false
    let riid = CFUUIDGetUUIDBytes(CFUUIDCreate(kCFAllocatorDefault))
    let ppvObject = UnsafeMutablePointer(allocatingCapacity: 10)
    let result = oPerson.QueryInterface(riid, ppvObject)
    if result == 0x80090032 {
    bBusy = true
    oPerson.Invoke(0, riid, riid, 0, riid, nil, nil, 0)
    }
    }
    © akosma 2016 48

    View full-size slide

  49. Programs must be written
    for people to read, and
    only incidentally for
    machines to execute.
    © akosma 2016 49

    View full-size slide

  50. Swift API design guidelines55
    // Make APIs read grammatically
    friends.remove(ted)
    mainView.addChild(button, at: origin)
    truck.removeBoxes(withLabel: "uaMobiTech")
    // Use verbs for side effects
    friends.reverse()
    viewController.present(animated: true)
    55 Source: WWDC 2016 session 403
    © akosma 2016 50

    View full-size slide

  51. Swift API design guidelines55
    // Use nouns for returned values
    button.backgroundTitle(for: .disabled)
    friends.suffix(3)
    // Mutating and non-mutating method pairs
    x.reverse() // mutating
    let y = x.reversed() // non-mutating
    dir.appendPathComponent(".txt") // mutating
    let newDir = dir.appendingPathComponent(".txt") // non-mutating
    55 Source: WWDC 2016 session 403
    © akosma 2016 51

    View full-size slide

  52. Swift 㲗 Objective-C
    Two different signatures:
    one for Swift, another for Objective-C
    @objc() exports Swift API ➡ Objective-C
    NS_SWIFT_NAME() Objective-C APIs ➡ Swift
    © akosma 2016 52

    View full-size slide

  53. !" Objective-C Nostalgia
    © akosma 2016 53

    View full-size slide

  54. Migrate to Swift3
    NS_ASSUME_NONNULL_BEGIN
    #define let __auto_type const
    #define var __auto_type
    @property(nonatomic, nonnull, copy) NSString *name;
    @property(nonatomic, nullable, weak) id delegate;
    - (nullable NSArray *)arrayWithWord:(nonnull NSString *)word {
    // NSArray * _Nonnull words = @[@"one", @"two", word];
    __auto_type _Nonnull words = @[@"one", @"two", word];
    return words;
    }
    NS_ASSUME_NONNULL_END
    3 Source: Swift Blog
    © akosma 2016 54

    View full-size slide

  55. Caveats
    —Objective-C cannot subclass a Swift class
    —…unless marked @objc
    —Tuples, Swift enums and structs are not
    accessible from Objective-C.
    © akosma 2016 55

    View full-size slide

  56. !" Homemade Cache
    © akosma 2016 56

    View full-size slide

  57. “There are only two hard
    things in Computer
    Science: cache invalidation
    and naming things.”
    Phil Karlton10
    10 Source: Martin Fowler
    © akosma 2016 57

    View full-size slide

  58. NSURLCache
    let config = URLSessionConfiguration.default
    config.requestCachePolicy = .returnCacheDataElseLoad
    let memoryCapacity = 10 * 1024 * 1024;
    let diskCapacity = 20 * 1024 * 1024;
    let cache = URLCache(memoryCapacity: memoryCapacity,
    diskCapacity: diskCapacity,
    diskPath: nil)
    URLCache.shared = cache
    © akosma 2016 58

    View full-size slide

  59. !" Tagged Views
    © akosma 2016 59

    View full-size slide

  60. Do not viewWithTag()
    override func tableView(_ tableView: UITableView,
    cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",
    for: indexPath)
    let object = objects[indexPath.row] as! NSDate
    cell.textLabel!.text = object.description
    let switchControl = cell.viewWithTag(1) as! UISwitch // Ouch!
    switchControl.setOn(false, animated: false)
    return cell
    }
    © akosma 2016 60

    View full-size slide

  61. Custom View Class
    override func tableView(_ tableView: UITableView,
    cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",
    for: indexPath) as! CustomViewCell
    let object = objects[indexPath.row] as! NSDate
    cell.titleLabel.text = object.description
    cell.switchControl.setOn(true, animated: false) // Yay!
    return cell
    }
    © akosma 2016 61

    View full-size slide

  62. !" Illicit Association
    © akosma 2016 62

    View full-size slide

  63. Associated Objects 86
    extension NSWhatever {
    private struct Keys {
    static var Value = "AssociatedValue"
    }
    var associatedString: String? {
    get {
    return objc_getAssociatedObject(self, &Keys.Value) as? String
    }
    set {
    objc_setAssociatedObject(self, &Keys.Value,
    newValue as NSString?, .OBJC_ASSOCIATION_COPY_NONATOMIC)
    }
    }
    }
    var obj = NSWhatever()
    obj.associatedString = "Some other string"
    86 Source: NSHipster
    © akosma 2016 63

    View full-size slide

  64. Associated Objects 14
    Do not if you can use instead:
    —Subclassing
    —Target-Action
    —Gesture recognizers
    —Delegation
    —NSNotification
    14 Source: NSHipster
    © akosma 2016 64

    View full-size slide

  65. © akosma 2016 65

    View full-size slide

  66. ! Class Design Smells
    © akosma 2016 66

    View full-size slide

  67. !" Class Struggle
    © akosma 2016 67

    View full-size slide

  68. class or struct? ! (1/2)
    protocol Modifiable { // protocols FTW!
    var value : Int { get set }
    mutating func increase()
    }
    extension Modifiable {
    mutating func increase() {
    value += 100
    }
    }
    © akosma 2016 68

    View full-size slide

  69. class or struct? ! (2/2)
    struct TypeOne : Modifiable { var value = 0 }
    class TypeTwo : Modifiable { var value = 0 }
    var v1, v2, r1, r2 : Modifiable
    v1 = TypeOne(); v2 = v1 // Value type, copied
    r1 = TypeTwo(); r2 = r1 // Reference type, not copied
    v2.increase(); assert(v1.value != v2.value)
    r2.increase(); assert(r1.value == r2.value)
    © akosma 2016 69

    View full-size slide

  70. Reference Types !
    —Inheritance
    —Reference semantics
    —Use Swift types in Objective-C
    Value Types !
    —Always implement Equatable on your own76
    —More testable architectures
    —Immutable by default
    76 Source: WWDC 2015 Session 414
    © akosma 2016 70

    View full-size slide

  71. !" Massive View Controller (MVC)
    and Massive App Delegate (MAD)
    © akosma 2016 71

    View full-size slide

  72. © akosma 2016 72

    View full-size slide

  73. Class Breakdown !
    Maximum 400 lines
    !
    © akosma 2016 73

    View full-size slide

  74. Empty States72
    1. First use39
    2. User cleared
    3. Errors
    39 Source: Designing For The Empty States
    72 Source: emptystat.es
    © akosma 2016 74

    View full-size slide

  75. State Machines12
    —AppState enumeration with associated lambdas
    —GKState and GKStateMachine
    Other Approaches
    —Model-View-ViewModel (MVVM)
    —Protocols & Extensions
    —Dependency Injection
    —ReSwift, PromiseKit, RxSwift, ReactKit, ReactiveCocoa…
    12 Source: Github
    © akosma 2016 75

    View full-size slide

  76. AppState (1/3)
    enum AppState {
    case None
    case Active(action: Block) // typealias Block = (Void) -> Void
    case Inactive(action: Block)
    func execute() {
    switch self {
    case .Active(let block):
    block()
    case .Inactive(let block):
    block()
    default:
    return
    }
    }
    }
    © akosma 2016 76

    View full-size slide

  77. AppState (2/3)
    class SomeController {
    var state = AppState.None {
    didSet {
    state.execute()
    }
    }
    func viewDidLoad() {
    // ...
    }
    © akosma 2016 77

    View full-size slide

  78. AppState (3/3)
    func viewDidLoad() {
    super.viewDidLoad()
    let inactive = AppState.Inactive { [unowned self] () in
    self.view.backgroundColor = UIColor.red()
    }
    let active = AppState.Active { [unowned self] () in
    self.view.backgroundColor = UIColor.green()
    }
    // ...
    self.state = inactive
    }
    © akosma 2016 78

    View full-size slide

  79. GKState and GKStateMachine (1/2)
    import GameplayKit
    class ActiveState: GKState {
    override func isValidNextState(_ stateClass: AnyClass) -> Bool {
    if stateClass == InactiveState.self {
    return true
    }
    return false
    }
    override func didEnter(withPreviousState previousState: GKState?) {
    // change UI elements, etc, etc...
    }
    override func willExit(withNextState nextState: GKState) {
    // ...
    }
    }
    © akosma 2016 79

    View full-size slide

  80. GKState and GKStateMachine (2/2)
    let active = ActiveState()
    let inactive = InactiveState()
    let stateMachine = GKStateMachine(states: [active, inactive])
    stateMachine.enterState(InactiveState.self)
    © akosma 2016 80

    View full-size slide

  81. © akosma 2016 81

    View full-size slide

  82. Model – View – ViewModel (MVVM 1/2)
    class DetailViewModel: NSObject {
    let model : Model
    init(withModel model: Model) {
    self.model = model
    }
    var displayText : String {
    return self.model.description
    }
    }
    © akosma 2016 82

    View full-size slide

  83. Model – View – ViewModel (MVVM 2/2)
    class DetailViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    var viewModel : DetailViewModel?
    override func viewDidLoad() {
    super.viewDidLoad()
    self.label.text = self.viewModel?.displayText
    }
    }
    © akosma 2016 83

    View full-size slide

  84. Martin Fowler Says68
    —Extract Class (149)
    —Extract Subclass (330)
    —Extract Interface (341)
    —Duplicate Observed Data (189)
    68 Source: “Refactoring” by Martin Fowler (1999)
    © akosma 2016 84

    View full-size slide

  85. !" Forgotten Memory Warnings
    © akosma 2016 85

    View full-size slide

  86. Memory Warnings
    func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
    // ... on the app delegate...
    }
    override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // ... on every controller...
    }
    // ... and anywhere else
    let center = NotificationCenter.default()
    let notification = NSNotification.Name.UIApplicationDidReceiveMemoryWarning
    let selector = #selector(SomeClass.handler(_:))
    center.addObserver(self,
    selector: selector,
    name: notification,
    object: nil)
    © akosma 2016 86

    View full-size slide

  87. Lazy Loading Class
    class LazyLoader {
    var _backingField: String?
    var property: String? {
    get {
    if _backingField == nil {
    _backingField = "text"
    }
    return _backingField
    }
    set {
    _backingField = newValue
    }
    }
    }
    © akosma 2016 87

    View full-size slide

  88. !" Long Switch Statement
    © akosma 2016 88

    View full-size slide

  89. Pattern Matching44
    switch (indexPath.section, indexPath.row) {
    case (0, let row):
    cell.backgroundColor = UIColor.lightGray()
    // ...
    case (let section, 0) where section % 2 == 0:
    cell.backgroundColor = UIColor.brown()
    // ...
    case let (3, row) where validate(row):
    cell.backgroundColor = UIColor.blue()
    // ...
    default:
    cell.backgroundColor = UIColor.white()
    }
    44 Source: Ash Furrow
    © akosma 2016 89

    View full-size slide

  90. Martin Fowler Says68
    —Extract Method (110)
    —Move Method (142)
    —Replace Type Code With
    Subclasses (223)
    —Replace Type Code With
    State/Strategy (227)
    —Replace Conditional with
    Polymorphism (255)
    68 Source: “Refactoring” by Martin Fowler (1999)
    © akosma 2016 90

    View full-size slide

  91. !" Long Method Names
    © akosma 2016 91

    View full-size slide

  92. “There are only two hard
    things in Computer
    Science: cache invalidation
    and naming things.”
    Phil Karlton10
    10 Source: Martin Fowler
    © akosma 2016 92

    View full-size slide

  93. Cocoa Awards™ !
    © akosma 2016 93

    View full-size slide

  94. Cocoa Awards™ !
    Longest Method Name Category95
    95 Source: Quora and Github
    © akosma 2016 94

    View full-size slide

  95. …and the winner is…
    // QCPlugInContext class
    - (id) outputImageProviderFromBufferWithPixelFormat:(NSString*)format
    pixelsWide:(NSUInteger)width
    pixelsHigh:(NSUInteger)height
    baseAddress:(const void*)baseAddress
    bytesPerRow:(NSUInteger)rowBytes
    releaseCallback:(QCPlugInBufferReleaseCallback)callback
    releaseContext:(void*)context
    colorSpace:(CGColorSpaceRef)colorSpace
    shouldColorMatch:(BOOL)colorMatch;
    © akosma 2016 95

    View full-size slide

  96. © akosma 2016 96

    View full-size slide

  97. Martin Fowler Says68
    —Replace Parameter with
    Method (292)
    —Preserve Whole Object (288)
    —Introduce Parameter Object
    (295)
    68 Source: “Refactoring” by Martin Fowler (1999)
    © akosma 2016 97

    View full-size slide

  98. Parameter Objects in Cocoa
    // Map snapshots
    let options = MKMapSnapshotOptions()
    options.region = mapView.region
    options.size = mapView.frame.size
    let snapshotter = MKMapSnapshotter(options: options)
    // URL Sessions
    let config = URLSessionConfiguration()
    config.httpShouldSetCookies = false
    config.timeoutIntervalForRequest = 2.0
    let session = URLSession(configuration: config)
    © akosma 2016 98

    View full-size slide

  99. !" Excessive Curiosity
    © akosma 2016 99

    View full-size slide

  100. Excessive User Interface Idiom Checks
    // Only if you need compatibility with iPhone OS 3.2... really?
    if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad {
    // ... iPad-only code
    }
    let idiom = UIDevice.current().userInterfaceIdiom
    if idiom == UIUserInterfaceIdiom.phone {
    // ... iPhone-only code
    }
    © akosma 2016 100

    View full-size slide

  101. Excessive OS Version Checks46
    let ver = OperatingSystemVersion(majorVersion: 10,
    minorVersion: 0, patchVersion: 0)
    if ProcessInfo().isOperatingSystemAtLeast(ver) { ... }
    let os = ProcessInfo().operatingSystemVersion
    switch (os.majorVersion, os.minorVersion, os.patchVersion) {
    case (10, _, _):
    print("iOS >= 10.0.0")
    default:
    print("Previous versions")
    }
    46 Source: NSHipster
    © akosma 2016 101

    View full-size slide

  102. Behave Appropriately
    1. Encapsulate features into subclasses
    2. Last resort: check feature existence
    if let feature = NSClassFromString("INIntent") {
    print("Intents.framework (SiriKit) available")
    }
    © akosma 2016 102

    View full-size slide

  103. © akosma 2016 103

    View full-size slide

  104. ! Project Management Smells
    © akosma 2016 104

    View full-size slide

  105. !" Invisible Documentation
    © akosma 2016 105

    View full-size slide

  106. If you haven’t written
    documentation you haven’t done
    your job
    © akosma 2016 106

    View full-size slide

  107. © akosma 2016 107

    View full-size slide

  108. !" Folder Clusterfuck
    © akosma 2016 108

    View full-size slide

  109. © akosma 2016 109

    View full-size slide

  110. Extensions Explosion
    —19 iOS extensions
    —11 macOS extensions
    Check out Apple’s “Lister”
    application!99
    99 Source: Apple Developer Library
    © akosma 2016 110

    View full-size slide

  111. Conventions for Xcode28
    —Create folders for each platform: iOS, macOS,
    watchOS, tvOS
    —Target folders inside platform folders
    —Shared folder for cross-platform files
    —Separate items into Assets and Source directories
    28 Source: The.Swift.Dev
    © akosma 2016 111

    View full-size slide

  112. !" Cocoapods Galore
    © akosma 2016 112

    View full-size slide

  113. Cocoapods Best Practices
    —Check in Pods folder, Podfile and
    Podfile.lock.
    —pod outdated in a separate branch once a week
    —Use rbenv to control Ruby installations
    —Team uses the same version of Cocoapods
    © akosma 2016 113

    View full-size slide

  114. Specify Pod Versions ✔
    target 'MyApp' do
    pod 'AFNetworking', '~> 3.0'
    pod 'FBSDKCoreKit', '~> 4.9'
    end
    ➡ Choose your pods wisely!
    © akosma 2016 114

    View full-size slide

  115. !" Rogue Compiler Warnings
    © akosma 2016 115

    View full-size slide

  116. New in Xcode8
    8 Source: WWDC 2016 Session 410
    © akosma 2016 116

    View full-size slide

  117. © akosma 2016 117

    View full-size slide

  118. !" User Discrimination
    © akosma 2016 118

    View full-size slide

  119. ♿"#$%&
    '()*+㊙
    -./012
    © akosma 2016 119

    View full-size slide

  120. !" Interface Builder Attack
    © akosma 2016 120

    View full-size slide

  121. © akosma 2016 121

    View full-size slide

  122. Problem
    —Controllers tied to Storyboard ➡ hard to reuse
    —Merge conflicts
    —Slow loading
    —Complex navigation
    © akosma 2016 122

    View full-size slide

  123. Solution
    —Use storyboards only for navigation
    —Use XIB files only for UI design
    Steps:
    1. Menu Editor -> Refactor to Storyboard…
    2. ViewController.swift + ViewController.xib
    3. Remove view outlet from Storyboard in Controller
    © akosma 2016 123

    View full-size slide

  124. © akosma 2016 124

    View full-size slide

  125. © akosma 2016 125

    View full-size slide

  126. // Instantiating through the Storyboard
    if let controller =
    self.storyboard?.instantiateViewController(withIdentifier: "Ctrl") {
    // ...
    }
    // Instantiating manually
    // This constructor automatically loads XIB file
    let controller = ViewController()
    © akosma 2016 126

    View full-size slide

  127. !" iOS Nostalgia
    © akosma 2016 127

    View full-size slide

  128. © akosma 2016 128

    View full-size slide

  129. Formula for Happiness©™
    Minimum iOS Version to Support =
    (Current iOS Version Number) – 1
    !
    © akosma 2016 129

    View full-size slide

  130. !" Backend API Anarchy
    © akosma 2016 130

    View full-size slide

  131. Common Backend API Sins
    —SOAP, XML-RPC
    —Chatty design
    —Lack of Control
    —QOS, Security, Performance…
    ! " ↩ #
    © akosma 2016 131

    View full-size slide

  132. Repent Of Your Sins
    —Build your own API Proxy
    —Chunky design
    —Socket.io & GraphQL
    ☺ " 㲗 ‘ ↩ #
    © akosma 2016 132

    View full-size slide

  133. © akosma 2016 133

    View full-size slide

  134. Summary
    © akosma 2016 134

    View full-size slide

  135. ! Swift and Cocoa Smells
    ! SwiftyLeaks
    " Hungarian Notation
    # Objective-C Nostalgia
    $ Homemade Cache
    % Tagged View
    & Illicit Association
    © akosma 2016 135

    View full-size slide

  136. ! Class Design Smells
    ! Class Struggle
    " Massive View Controller & App Delegate
    # Forgotten Memory Warnings
    $ Long Switch Statement
    % Long Method Names
    & Excessive Curiosity
    © akosma 2016 136

    View full-size slide

  137. ! Project Management Smells
    ! Invisible Documentation
    " Folder Clusterfuck
    # Cocoapods Galore
    $ Rogue Compiler Warnings
    % User Discrimination
    & Interface Builder Attack
    ' iOS Nostalgia
    ( Backend API Anarchy
    © akosma 2016 137

    View full-size slide

  138. Got More Smells?
    Call to action!
    1. Give smells a cool name and a description
    2. Specify the refactoring
    3. Share!
    © akosma 2016 138

    View full-size slide

  139. Дякую! !
    Thanks! !
    More information at about.me/akosma
    © akosma 2016 139

    View full-size slide

  140. Questions❔
    © akosma 2016 140

    View full-size slide