Stupid Enum Tricks in Swift and Kotlin - We Are Developers, Vienna, Austria, May 2018

Stupid Enum Tricks in Swift and Kotlin - We Are Developers, Vienna, Austria, May 2018

Enums in Swift and enum classes in Kotlin are extremely powerful ways to deal with a known set of possible values. Learn how to use these tools to help you cleaner, safer code, and how to push the boundaries of what's possible with enums.

C4861b1dfdf3bbb21faec4a1acdf183d?s=128

Ellen Shapiro

May 16, 2018
Tweet

Transcript

  1. 1.

    STUPID ENUM TRICKS IN SWIFT AND KOTLIN WE ARE DEVELOPERS

    | VIENNA, AUSTRIA | MAY 2018 BAKKENBAECK.COM | JUSTHUM.COM | DESIGNATEDNERD.COM BY ELLEN SHAPIRO | @DESIGNATEDNERD
  2. 6.

    GIF

  3. 7.

    GIF

  4. 13.

    SWIFT enum ColorName: String { case red case orange case

    yellow case green case blue case violet }
  5. 15.

    SWIFT func colorForName(_ name: String) -> UIColor? { switch name

    { case ColorName.red.rawValue: return UIColor.red case ColorName.orange.rawValue: return UIColor.orange // same for other ColorName cases default: return nil }
  6. 21.

    SWIFT STANDARD LIBRARY public enum ComparisonResult : Int { case

    orderedAscending case orderedSame case orderedDescending }
  7. 29.

    SWIFT var currentColor: TrafficLightColor = .red switch currentColor { case

    .red: print("STOP RIGHT THERE") case .yellow: print("Whoa, slow down there, buddy.") case .green: print("Go, go, go!") }
  8. 30.

    SWIFT var currentColor: TrafficLightColor = .red switch currentColor { case

    .red: print("STOP RIGHT THERE") case .green: print("Go, go, go!") // Error: Unhandled case! }
  9. 31.

    SWIFT var currentColor: TrafficLightColor = .red switch currentColor { case

    .red: print("STOP RIGHT THERE") case .yellow, .green: print("Go, go, go!") }
  10. 32.

    KOTLIN var currentColor = TrafficLightColor.Red when (currentColor) { TrafficLightColor.Red ->

    println("STOP RIGHT THERE") TrafficLightColor.Yellow -> println("Whoa, slow down there, buddy.") TrafficLightColor.Green -> println("Go, go, go!") }
  11. 35.

    KOTLIN Instance Gets You Example instance.name String value of case's

    written name Days.THURSDAY.name is "THURSDAY", Days.Thursday.name is "Thursday" instance.ordinal Index of case in list of cases
  12. 36.

    KOTLIN Type Gets You Type.valueOf(string: String) Enum value of string,

    or null Type.values() Generated list of all values in the enum class
  13. 42.

    SWIFT enum SettingsSection: Int { case profile // 0 case

    contact // 1 case legalese // 2 case logout // 3 }
  14. 43.

    SWIFT enum SettingsSection: Int { case profile // 0 case

    legalese // 1 case contact // 2 case logout // 3 }
  15. 46.

    SWIFT enum JSONKey: String { case user_name case email_address case

    latitude = "lat" case longitude = "long" }
  16. 47.

    SWIFT Codable class User: Codable { let userName: String let

    email: String let latitude: Double let longitude: Double enum CodingKeys: String, CodingKey { case userName = "user_name" case email = "email_address" case latitude = "lat" case longitude = "long" } }
  17. 49.
  18. 51.

    SWIFT enum SettingsSection: Int, CaseIterable { case profile case legalese

    case contact case logout } // Automatically generated: static var allCases: [SettingsSection]
  19. 52.

    SWIFT func numberOfSectionsInTableView(_ tableView: UITableView) -> Int { return SettingsSection.allCases.count

    } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String { let section = SettingsSection.allCases[section] return section.localizedTitle }
  20. 56.

    SWIFT enum LandingScreenButton { case signIn case signUp case viewTerms

    var localizedTitle: String { switch self { case .signIn: return NSLocalizedString("Sign In", "Sign in button title") case .signUp: return NSLocalizedString("Sign Up", "Sign up button title") case .viewTerms: return NSLocalizedString("View Terms & Conditions", "Title for button to view legalese") } }
  21. 57.

    SWIFT public enum LandingScreenButton { case signIn case signUp case

    viewTerms public var localizedTitle: String { switch self { case .signIn: return NSLocalizedString("Sign In", "Sign in button title") case .signUp: return NSLocalizedString("Sign Up", "Sign up button title") case .viewTerms: return NSLocalizedString("View Terms & Conditions", "Title for button to view legalese") } }
  22. 67.

    SWIFT / IOS: AFTER public enum Asset: String { case

    cinnamon_rolls case innocent case no case snack case window var image: UIImage { return UIImage(named: self.rawValue)! } }
  23. 68.

    SWIFT / IOS: TESTS! func testAllAssetsAreThere() { XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named:

    Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) }
  24. 69.

    SWIFT / IOS: TESTS! func testAllAssetsAreThere() { XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named:

    Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) XCTAssertNotNil(UIImage(named: Asset.cinnamon_rolls.rawValue)) } !
  25. 70.

    SWIFT / IOS: TESTS! func testAllAssetsAreThere() { for asset in

    Asset.allCases { XCTassertNotNil(UIImage(named: asset.rawValue), "Image for \(asset.rawValue) was nil!") } }
  26. 71.

    SWIFT / IOS: TESTS! func testAllAssetsAreThere() { for asset in

    Asset.allCases { XCTassertNotNil(UIImage(named: asset.rawValue), "Image for \(asset.rawValue) was nil!") } } !!!!!!!
  27. 77.

    SWIFT: BEFORE @IBAction private func signIn() { guard self.validates() else

    { return } performSegue(withIdentifier: "toCatsLoggedIn", sender: nil) }
  28. 78.

    SWIFT: THE ENUM enum MainStoryboardSegue: String { case toSignIn case

    toSignUp case toTerms case toCats case toCatsLoggedIn }
  29. 81.

    SWIFT: THE PROTOCOL WITH A GENERIC EXTENSION FUNCTION protocol SeguePerforming

    { var rawValue: String { get } } extension UIViewController { func perform<T: SeguePerforming>(segue: T, sender: Any? = nil) { performSegue(withIdentifier: segue.rawValue, sender: sender) } }
  30. 82.

    SWIFT: THE ENUM, REVISED enum MainStoryboardSegue: String, SeguePerforming { case

    toSignIn case toSignUp case toTerms case toCats case toCatsLoggedIn }
  31. 83.

    SWIFT: AFTER @IBAction private func signIn() { guard self.validates() else

    { return } self.perform(MainStoryboardSegue.toCats) }
  32. 85.

    SWIFT enum DownloadState { case notStarted, case downloading(let progress: Float)

    case success(let data: Data) case error(let error: Error?) }
  33. 91.

    KOTLIN sealed class DownloadStateWithInfo { class NotStarted: DownloadStateWithInfo() class Downloading(val

    progress: Float): DownloadStateWithInfo() class Succeeded(val data: ByteArray): DownloadStateWithInfo() }
  34. 92.

    KOTLIN sealed class DownloadStateWithInfo { class NotStarted: DownloadStateWithInfo() class Downloading(val

    progress: Float): DownloadStateWithInfo() class Succeeded(val data: ByteArray): DownloadStateWithInfo() class Failed(val error: Error): DownloadStateWithInfo() }
  35. 95.

    KOTLIN inline fun <reified T: Enum<T>> T.next(): T { val

    currentIndex = this.ordinal val nextIndex = currentIndex + 1 val allValues = enumValues<T>() return if (nextIndex >= allValues.size) { allValues[0] } else { allValues[nextIndex] } }
  36. 96.

    KOTLIN inline fun <reified T: Enum<T>> T.next(): T { //

    val currentIndex = this.ordinal // val nextIndex = currentIndex + 1 // val allValues = enumValues<T>() // return if (nextIndex >= allValues.size) { // allValues[0] // } else { // allValues[nextIndex] // } }
  37. 97.

    KOTLIN inline fun <reified T: Enum<T>> T.next(): T { //

    val currentIndex = this.ordinal // val nextIndex = currentIndex + 1 val allValues = enumValues<T>() // return if (nextIndex >= allValues.size) { // allValues[0] // } else { // allValues[nextIndex] // } }
  38. 98.

    KOTLIN inline fun <reified T: Enum<T>> T.next(): T { //

    val currentIndex = this.ordinal // val nextIndex = currentIndex + 1 // val allValues = enumValues<T>() return if (nextIndex >= allValues.size) { allValues[0] } else { allValues[nextIndex] } }
  39. 103.

    KOTLIN sealed class DownloadStateWithInfo { class NotStarted: DownloadStateWithInfo() class Downloading(val

    progress: Float): DownloadStateWithInfo() class Succeeded(val data: ByteArray): DownloadStateWithInfo() class Failed(val error: Error): DownloadStateWithInfo() }
  40. 107.

    SWIFT: GOOD enum LandingScreenButton { // Stuff we saw before

    var localizedTitle: String { switch self { case .signIn: return NSLocalizedString("Sign In", "Sign in button title") case .signUp: return NSLocalizedString("Sign Up", "Sign up button title") case .viewTerms: return NSLocalizedString("View Terms & Conditions", "Title for button to view legalese") } }
  41. 108.

    SWIFT: OVER THE TOP enum LandingScreenButton { // Stuff we

    saw before func handleClick(in viewController: UIViewController) { switch self { case .signIn: viewController.navigationController? .pushViewController(SignInViewController(), animated: true) case .signUp: viewController.navigationController? .pushViewController(RegistrationViewController(), animated: true) case .viewTerms: viewController.present(LegaleseViewController(), animated: true) } } }
  42. 113.

    OBLIGATORY SUMMARY SLIDE > Enums are a great way to

    represent distinct state > Limit your cases, limit your bugs
  43. 114.

    OBLIGATORY SUMMARY SLIDE > Enums are a great way to

    represent distinct state > Limit your cases, limit your bugs > Value determined by the current case -> computed var
  44. 115.

    OBLIGATORY SUMMARY SLIDE > Enums are a great way to

    represent distinct state > Limit your cases, limit your bugs > Value determined by the current case -> computed var > Generated enums help reduce stringly-typed code
  45. 116.

    OBLIGATORY SUMMARY SLIDE > Enums are a great way to

    represent distinct state > Limit your cases, limit your bugs > Value determined by the current case -> computed var > Generated enums help reduce stringly-typed code > Don't forget about separation of concerns
  46. 118.

    LINKS! > The Swift book's section on Enums: https://developer.apple.com/library/ content/documentation/Swift/Conceptual/

    Swift_Programming_Language/ Enumerations.html > Kotlin Enum Class documentation: https://kotlinlang.org/docs/reference/ enum-classes.html