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

Lost in maintenance in Swift

Lost in maintenance in Swift

How to support unsupportable :)

Paul Taykalo

July 22, 2017
Tweet

More Decks by Paul Taykalo

Other Decks in Programming

Transcript

  1. Lost in maintenance: criteria of maintainable in Swift by Paul

    Taykalo, Stanfy Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 1
  2. Plan » Derermining project maintainability » Maintaining unmaintainable » Rewriting?

    » Speaking about maintainability Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 2
  3. We've got a project to check Be objective Lost in

    maintenance, by Paul Taykalo, #cocoaheadskyiv 9
  4. We decided not to take the project Lost in maintenance,

    by Paul Taykalo, #cocoaheadskyiv 11
  5. So, you've got a project First steps Lost in maintenance,

    by Paul Taykalo, #cocoaheadskyiv 15
  6. Are there any tests? You know the answer :( Lost

    in maintenance, by Paul Taykalo, #cocoaheadskyiv 18
  7. Are there any tests? You know the answer :( Lost

    in maintenance, by Paul Taykalo, #cocoaheadskyiv 19
  8. One (two) thing at the time Lost in maintenance, by

    Paul Taykalo, #cocoaheadskyiv 26
  9. Semantic Meaning if item.row == 2 { ... } Lost

    in maintenance, by Paul Taykalo, #cocoaheadskyiv 33
  10. Semantic Meaning if item.row == 2 { ... } Lost

    in maintenance, by Paul Taykalo, #cocoaheadskyiv 34
  11. Semantic Meaning if api.version > 2 { ... } Lost

    in maintenance, by Paul Taykalo, #cocoaheadskyiv 35
  12. Semantic Meaning if api.version > 2 { ... } let

    supportsThrottling = api.version > 2 if supportsThrottling { ... } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 36
  13. Semantic Meaning if api.version > 2 { ... } extension

    Api { var supportsThrottling: Bool { return version > 2} } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 37
  14. Semantic Meaning if let object = notification.object as? UIViewController ,

    object == self { ... } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 38
  15. Semantic Meaning if let object = notification.object as? UIViewController ,

    object == self { ... } let notificationWasSentByUs = notification.object == self if notificationWasSentByUs { ... } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 39
  16. Semantic Meaning if let currentUser = User.currentUser(inContext: mainContext) , currentUser.username

    != nil && currentUser.apiToken != nil ... else if let currentUser = User.currentUser(inContext: mainContext) , currentUser.username == nil && currentUser.apiToken != nil ... else Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 40
  17. Semantic meaning let user = User.currentUser(inContext: self.appData.mainContext) let userHasValidUserName =

    user?.username != nil let userHasValidToken = user?.apiToken != nil Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 41
  18. Semantic meaning if userHasValidToken && userHasValidUserName ... else if userHasValidToken

    && userHasNoName ... else Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 42
  19. Always be careful with any changes func overloaded(value: Double) ->

    String { return "Double" } func overloaded(value: Any) -> String { return "Any" } let size: Double? = dictionary["size"] let value = size ?? 0.0 let complex = overloaded(size ?? 0.0) // Any let simple = overloaded(value) // Double Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 46
  20. What are these functions about? UIImage -> UIImage String ->

    String -> String URL -> UIImage Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 48
  21. Phantom types PhotoAlbumImage -> TumbnailImage UserID -> AddressID -> Address

    FileURL -> ThumbnailImage Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 50
  22. Phantom types struct FilteredImage { // this will be filtered

    only let image: UIImage } struct BigImage { // in memory image private(set) var inMemoryImage: UIImage? // stored private var storedURL: URL? var image: UIImage { // return from the memory // or load from the fs } } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 51
  23. Phantom types struct FileSystemURL { let url: NSURL // Failable

    initializer init?(url: NSURL) } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 52
  24. Phantom types » Concrete » Unique behaviour » Control Lost

    in maintenance, by Paul Taykalo, #cocoaheadskyiv 54
  25. Hell? vs Hell! struct User { let name: String? let

    token: String? } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 60
  26. Hell? vs Hell! struct UnverifiedUser {} struct User { let

    name: String } struct LoggedUser { let user: User let token: String // TokenString } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 61
  27. Hell? vs Hell! // First var itemsPerKey: [Key: Item] =

    [:] // Then var itemsPerKey: [Key: Item] = [:] var isDeleted: [Key: Bool] = [:] Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 62
  28. Hell? vs Hell! enum ItemState { case assigned(let item) case

    deleted var item: Item? { if .assigned(let item) = self { return item } return nil } } var itemsPerKey: [Key: ItemState] = [:] Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 63
  29. Hell? vs Hell! enum UploadingState { case notStarted case uploading(Foundation.Operation)

    case uploaded case failed(Error) func operation() -> Foundation.Operation? { switch self { case .uploading(let op): return op default: return nil } } func isUploading() -> Bool { if case .uploading = self { return true } return false } } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 64
  30. Hell? vs Hell! public enum Result<Value, Error: Swift.Error> { case

    success(Value) case error(Error) } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 65
  31. How to make changes in the project Lost in maintenance,

    by Paul Taykalo, #cocoaheadskyiv 68
  32. How to make changes in the project Lost in maintenance,

    by Paul Taykalo, #cocoaheadskyiv 69
  33. How to make changes in the project » What is

    going on? » Freeze current behaviour » Write down desired behaviour » Check » Make sure that all unrelated sidefects still there Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 70
  34. How to make changes in the project » Make tests

    on current behavor » Make change » Check Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 71
  35. But I cannot test it because... » I dont understand

    how class works » I cannot instantiate class in test env » I cannot test method - it's too bit » I cannot... Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 73
  36. . . It's too big for testing Lost in maintenance,

    by Paul Taykalo, #cocoaheadskyiv 74
  37. 'But I cannot' solutions » Test method in class »

    Make an helper and use it class Huge { func perform { if object.price as? String == } } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 75
  38. 'But I cannot' solutions class Huge { func perform {

    if let price = object.price as? String } } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 76
  39. 'But I cannot' solutions struct Price { init?(object: [String: Any])

    {} var priceToShow: String {} var discont: String {} } Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 77
  40. 'But I cannot' solutions protocol Dispatcher { func background(_ block:

    @escaping (() -> Void)) func main(_ block: @escaping (() -> Void)) func main_from_background(_ block: @escaping(() -> Void)) func background_delay(_ delay: Int64, block: @escaping (() -> Void)) func main_delay(_ delay: Int64, block: @escaping (() -> Void)) } // for everything else class DefaultDispatcher: Dispatcher {} // For tests class SyncDispatcher: Dispatcher {} Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 78
  41. 'But I cannot' solutions » Core Data » Views »

    Snaphots » Hardware? Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 79
  42. L - is for Logs Lost in maintenance, by Paul

    Taykalo, #cocoaheadskyiv 82
  43. N - is for "No hope without Logs and Measurements"

    Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 84
  44. Small history about scrolling » App is not using cache

    Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 87
  45. Small history about scrolling » ✅ Not using cache »

    ❌ Freezing Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 88
  46. Small history about scrolling » ✅ Not using cache »

    ✅ Freezing (disk cacke) » ❌ Freezing ( after MW) Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 89
  47. Small history about scrolling » ✅ Not using cache »

    ✅ Freezing (disk cacke) » ✅ Freezing ( after MW) » ❌ Lags (Jpeg Decoding) Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 90
  48. Small history about scrolling » ✅ Not using cache »

    ✅ Freezing (disk cacke) » ✅ Freezing ( after MW) » ✅ Lags (Jpeg Decoding) » ❌ Out of memory crashes Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 91
  49. Small history about scrolling » ✅ Not using cache »

    ✅ Freezing (disk cacke) » ✅ Freezing ( after MW) » ✅ Lags (Jpeg Decoding) » ❌ Out of memory crashes » ??????? Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 92
  50. Rewriting » Modularity » Testing! » Enclosing » Unification Lost

    in maintenance, by Paul Taykalo, #cocoaheadskyiv 97
  51. Modularity What if I tell you that there can be

    more than one storyboard? Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 98
  52. Testing Start writing tests :( - There's no time for

    it - I cannot sell it to the customer - I am supporting a legacy application without unit tests. - QA and User Acceptance Testing is far more effective in finding bugs - I don’t know how to unit test, or I don’t know how to write good unit tests Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 99
  53. Testing There'll be no failed tests if there's no tests

    Lost in maintenance, by Paul Taykalo, #cocoaheadskyiv 100
  54. Lost in maintenance by Paul Taykalo, Stanfy @TT_Kilew Lost in

    maintenance, by Paul Taykalo, #cocoaheadskyiv 108