$30 off During Our Annual Pro Sale. View Details »

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.

Mike Zornek

May 12, 2016
Tweet

More Decks by Mike Zornek

Other Decks in Technology

Transcript

  1. Swift Code Patterns
    May 12, 2016 • Mike Zornek

    View Slide

  2. Introductions

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. Questions, anytime.

    View Slide

  8. What’s Inside?

    View Slide

  9. NSLocalizedString()

    View Slide

  10. NSLocalizedString("Job Request",
    comment: "header text for information regarding a job")

    View Slide

  11. NSLocalizedString("shopping-cart.create-order.button",
    comment: "Title for footer button in Shopping Cart")

    View Slide

  12. TODO Comments

    View Slide

  13. // TODO: Add proper error handling.

    View Slide

  14. // TODO: Add proper error handling.
    // https://github.com/bignerdranch/project-x/issues/39

    View Slide

  15. DRY

    View Slide

  16. Storyboards

    View Slide

  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"
    }

    View Slide

  18. Filenames
    URLs
    Preference Names
    JSON keys

    View Slide

  19. Extensions

    View Slide

  20. UIColor

    View Slide

  21. Forgive my
    indentation.

    View Slide

  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)

    View Slide

  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)
    }
    }

    View Slide

  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

    View Slide

  25. struct DayTheme {
    static let backgroundColor = UIColor(hex: 0xfefefe)
    static let textColor = UIColor(hex: 0x121212)
    static let highlightColor = UIColor.redColor()
    }
    greetingLabel.textColor = DayTheme.textColor

    View Slide

  26. UITableView

    View Slide

  27. let cell =
    tableView.dequeueReusableCellWithIdentifier("CustomCell",
    forIndexPath: indexPath) as! CustomCell
    let cell = tableView.dequeue(cellOfType: CustomCell.self,
    indexPath: indexPath)

    View Slide

  28. extension UITableView {
    func dequeue(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)

    View Slide

  29. Custom Table Cells

    View Slide

  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))
    }
    }

    View Slide

  31. protocol NibLoadable: class {
    static var nibName: String { get }
    static var nib: UINib { get }
    func createView(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(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
    }
    }

    View Slide

  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
    }
    }

    View Slide

  33. Enum

    View Slide

  34. Humanize TableView
    Layout

    View Slide

  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)
    }
    }
    }

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

  38. Result

    View Slide

  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)")
    }

    View Slide

  40. UIImage

    View Slide

  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)

    View Slide

  42. Dependency
    Injection
    Tell, Don’t Ask

    View Slide

  43. View Slide

  44. Not Easy :(

    View Slide

  45. extension DashboardViewController {
    func configure(toDoListStore: ToDoListStore, delegate:
    DashboardViewControllerDelegate) {
    self.toDoListStore = toDoListStore
    self.delegate = delegate
    }
    }

    View Slide

  46. Launch / Debug
    Menu

    View Slide

  47. View Slide

  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]
    }

    View Slide

  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 { ... }

    View Slide

  50. Many DataSources

    View Slide

  51. View Slide

  52. View Slide

  53. View Slide

  54. View Slide

  55. View Slide

  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/

    View Slide

  57. Learn More?

    View Slide

  58. View Slide

  59. View Slide

  60. https://vimeo.com/144116310

    View Slide

  61. Thank you.
    Michael Zornek
    [email protected]
    @zorn

    View Slide