Slide 1

Slide 1 text

Swift Code Patterns May 12, 2016 • Mike Zornek

Slide 2

Slide 2 text

Introductions

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Questions, anytime.

Slide 8

Slide 8 text

What’s Inside?

Slide 9

Slide 9 text

NSLocalizedString()

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

TODO Comments

Slide 13

Slide 13 text

// TODO: Add proper error handling.

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

DRY

Slide 16

Slide 16 text

Storyboards

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Filenames URLs Preference Names JSON keys

Slide 19

Slide 19 text

Extensions

Slide 20

Slide 20 text

UIColor

Slide 21

Slide 21 text

Forgive my indentation.

Slide 22

Slide 22 text

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)

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

UITableView

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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)

Slide 29

Slide 29 text

Custom Table Cells

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Enum

Slide 34

Slide 34 text

Humanize TableView Layout

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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 }

Slide 37

Slide 37 text

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 }

Slide 38

Slide 38 text

Result

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

UIImage

Slide 41

Slide 41 text

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)

Slide 42

Slide 42 text

Dependency Injection Tell, Don’t Ask

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Not Easy :(

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Launch / Debug Menu

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Many DataSources

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

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/

Slide 57

Slide 57 text

Learn More?

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

https://vimeo.com/144116310

Slide 61

Slide 61 text

Thank you. Michael Zornek zorn@bignerdranch.com @zorn