Swift Code Patterns

Swift Code Patterns

An updated version of my older Code Patterns talk.

This talk starts of reviewing small recommendations and works it's way up to talking about extensions (including some examples taken from recent projects) and then finally about larger design patterns including: Dependency Injection, Launch/Debug Menus and Many DataSources / Scenarios.

68d48587fc806c2b35eb9ff0b7ad8115?s=128

Mike Zornek

May 12, 2016
Tweet

Transcript

  1. 3.
  2. 4.
  3. 5.
  4. 6.
  5. 15.

    DRY

  6. 17.

    struct AppNameStoryboards { static func logInStoryboard() -> UIStoryboard { return

    UIStoryboard(name: "LogIn", bundle: nil) } } // LoginViewController.swift private struct Segue { static let PINSetup = "showPinSetup" static let RetrieveUsername = "showRetrieveUsername" static let ResetPassword = "showResetPassword" static let ResendActivationLink = "showResendActivationLink" }
  7. 20.
  8. 22.

    let c1 = UIColor.redColor() let c2 = UIColor(hue: 0.8, saturation:

    0.8, brightness: 0.8, alpha: 1.0) let c3 = UIColor(red: 50/255, green: 25/255, blue: 32/255, alpha: 255/255) let c4 = UIColor(hex: 0xE6A024)
  9. 23.

    public extension UIColor { convenience init(hex: UInt32) { let red

    = CGFloat((hex >> 16) & 0xff) / 255 let green = CGFloat((hex >> 8) & 0xff) / 255 let blue = CGFloat((hex ) & 0xff) / 255 self.init(red: red, green: green, blue: blue, alpha: 1) } }
  10. 24.

    public extension UIColor { convenience init(bnr_hex: UInt32) { let red

    = CGFloat((hex >> 16) & 0xff) / 255 let green = CGFloat((hex >> 8) & 0xff) / 255 let blue = CGFloat((hex ) & 0xff) / 255 self.init(red: red, green: green, blue: blue, alpha: 1) } } bnr_hex
  11. 25.

    struct DayTheme { static let backgroundColor = UIColor(hex: 0xfefefe) static

    let textColor = UIColor(hex: 0x121212) static let highlightColor = UIColor.redColor() } greetingLabel.textColor = DayTheme.textColor
  12. 28.

    extension UITableView { func dequeue<V: UITableViewCell>(cellOfType type: V.Type, indexPath: NSIndexPath,

    reuseIdentifier: String? = nil) -> V { let identifier = reuseIdentifier ?? String(type) return dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! V } } String(type) // old way NSStringFromClass(type)
  13. 30.

    protocol NibLoadable: class { static var nibName: String { get

    } static var nib: UINib { get } } extension NibLoadable { static var nib: UINib { return UINib(nibName: nibName, bundle: NSBundle(forClass: self)) } }
  14. 31.

    protocol NibLoadable: class { static var nibName: String { get

    } static var nib: UINib { get } func createView<View: UIView where View: NibLoadable>(ofType _: View.Type) -> View } extension NibLoadable { static var nib: UINib { return UINib(nibName: nibName, bundle: NSBundle(forClass: self)) } // Useful for custom view designed in xibs, eg: table header view, section headers, etc. static func createView<View: UIView where View: NibLoadable>(ofType _: View.Type) -> View { let nib = View.nib let nibContents = nib.instantiateWithOwner(nil, options: nil) assert(nibContents.count == 1, "\(nib) should hold only a single view, but found \(nibContents.count) items: \(nibContents)") return nibContents.first as! View } }
  15. 32.

    class ChangePasswordCreateViewController: FormTableViewController { private lazy var tableHeaderView: FormTableHeaderView =

    { let headerView = createView(ofType: FormTableHeaderView.self) headerView.textLabel.text = NSLocalizedString("settings.change- password.create-new.header", comment: "Form table header text for new password creation.") Themer.themeDarkFormHeader(headerView) return headerView }() private func setupTableView() { // other stuff tableView.tableHeaderView = tableHeaderView } }
  16. 33.
  17. 35.

    class StoreDetailsController: UITableViewController { private enum CellType: String { case

    Map = "MapCell" case Title = "TitleCell" case Address = "AddressCell" case Phone = "PhoneCell" case StoreHours = "StoreHoursCell" } private var cells: [CellType] = [] private var store: Store! func configure(store: Store) { self.store = store cells = [.Map, .Title, .Address] if store.locationPhone != nil { cells.append(.Phone) } if store.storeHours != nil { cells.append(.StoreHours) } } }
  18. 36.

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cellType = cells[indexPath.row] let reuseIdentifier = cellType.rawValue let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) switch cellType { case .Map: print("custom config") case .Title: print("custom config") case .Address: print("custom config") case .Phone: print("custom config") case .StoreHours: print("custom config") } return cell }
  19. 37.

    enum UserProfileSections { case UsageDataSection case ContactInfoSection static let count

    = 2 } enum UsageDataRows { case NumberOfTweetsRow case NumberOfFollowersRow static let count = 2 } enum ContactInfoRows { case EmailRow case PhoneRow case PostalRow static let count = 3 }
  20. 38.
  21. 39.

    enum WebServiceResult { case Success(String) case Error(Int) } let result

    = downloadWebContent() switch result { case .Success(let content): print("Success content: \(content)") case .Error(let errorCode): print("Error code: \(errorCode)") }
  22. 40.
  23. 41.

    extension UIImage { enum AssetIdentifier: String { case Location =

    "icon-location-small-gray" case Phone = "icon-phone" case Star = "icon-fav-location-off" case StarOn = "icon-fav-location-on" case StarGrey = "icon-fav-location-small-gray" case Time = "icon-time" case ChevronDown = "icon-chevron-down" case Error = "icon-error" } convenience init!(_ assetIdentifier: AssetIdentifier) { self.init(named: assetIdentifier.rawValue) } } let icon = UIImage(.Star)
  24. 43.
  25. 47.
  26. 48.

    struct MenuItem { let title: String let details: String? let

    runAction: ((navigation: UINavigationController) -> ())? } struct MenuSection { let title: String let items: [MenuItem] } struct Menu { let title: String let sections: [MenuSection] }
  27. 49.

    extension Menu { static func appMemu() -> Menu { let

    uiColorDemo = MenuItem(title: "UIColor", details: "Building colors from hex values”, runAction: { (navigation) in let storyboard = UIStoryboard(name: "UIColorDemo", bundle: nil) let vc = storyboard.instantiateViewControllerWithIdentifier("UIColorDemoViewController") as! UIColorDemoViewController navigation.pushViewController(vc, animated: true) }) let demos = MenuSection(title: "Demos", items: [uiColorDemo]); let menu = Menu(title: "Menu", sections: [demos]) return menu } } class MenuViewController: UITableViewController { ... }
  28. 51.
  29. 52.
  30. 53.
  31. 54.
  32. 55.
  33. 56.

    Resources • Weather Demo, that illustrates some of the patterns

    discussed.
 https://github.com/zorn/WeatherDemo • Collection of source files, used to prep slides.
 https://github.com/zorn/CodePatternsScratchPad • https://www.bignerdranch.com/opensource/
  34. 58.
  35. 59.