Slide 1

Slide 1 text

HIDDEN GEMS IN SWIFT ! @JASDEV

Slide 2

Slide 2 text

IN OCTOBER 2015, I STARTED A TWITTER ACCOUNT CALLED @PUBLICEXTENSION

Slide 3

Slide 3 text

55 extensions so far 29 Playground pages GitHub

Slide 4

Slide 4 text

WHAT WE'LL COVER

Slide 5

Slide 5 text

> Literal convertible protocols > Experimental Operators > Apply mutating functions to immutable values > Attributes > Higher-level types for enum raw values > Metatypes > Custom pattern matching

Slide 6

Slide 6 text

LITERAL CONVERTIBLES > ArrayLiteralConvertible > BooleanLiteralConvertible > DictionaryLiteralConvertible > FloatLiteralConvertible > IntegerLiteralConvertible > StringLiteralConvertible

Slide 7

Slide 7 text

extension UIEdgeInsets: ArrayLiteralConvertible { public init(arrayLiteral elements: CGFloat...) { guard elements.count == 4 else { fatalError("4 CGFloats required!") } top = elements[0] left = elements[1] bottom = elements[2] right = elements[3] } } let insets: UIEdgeInsets = [10, 20, 10, 20] Swift Evolution: Tuple Convertible

Slide 8

Slide 8 text

HIGHER-LEVEL TYPES AS ENUMERATION RAW VALUES

Slide 9

Slide 9 text

"Raw values can be strings, characters, or any of the integer or floating-point number types. Each raw value must be unique within its enumeration declaration."

Slide 10

Slide 10 text

extension CGSize: StringLiteralConvertible { // Inspired by @terhechte public init(stringLiteral value: String) { let size = CGSizeFromString(value) self.init(width: size.width, height: size.height) } public init(extendedGraphemeClusterLiteral value: String) { let size = CGSizeFromString(value) self.init(width: size.width, height: size.height) } public init(unicodeScalarLiteral value: String) { let size = CGSizeFromString(value) self.init(width: size.width, height: size.height) } }

Slide 11

Slide 11 text

enum DevicesClass: CGSize { case iPhone4 = "{320, 480}" case iPhone5 = "{320, 568}" case iPhone6 = "{375, 667}" case iPhone6Plus = "{414, 736}" } let jasdevPhone = DevicesClass.iPhone6.rawValue // `CGSize`

Slide 12

Slide 12 text

APPLY MUTATING FUNCTIONS TO IMMUTABLE VALUES

Slide 13

Slide 13 text

// Credit to @jckarter func apply(transform: inout T -> U -> Void) -> T -> U -> T { return { a in { b in var c = a transform(&c)(b) return c } } } let first = [1, 2, 3] let second = [4, 5, 6] let third = apply(Array.appendContentsOf)(first)(second)

Slide 14

Slide 14 text

EXPERIMENTAL OPERATORS !

Slide 15

Slide 15 text

infix operator ∘ { associativity left precedence 100 } public func ∘(g: U -> V, f: T -> U) -> (T -> V) { return { g(f($0)) } } infix operator ∖ { associativity left precedence 140 } infix operator ∖= { associativity right precedence 90 assignment } infix operator ∪ { associativity left precedence 140 } infix operator ∪= { associativity right precedence 90 assignment } infix operator ∩ { associativity left precedence 150 } infix operator ∩= { associativity right precedence 90 assignment } infix operator ⨁ { associativity left precedence 140 } infix operator ⨁= { associativity right precedence 90 assignment } infix operator ∈ { associativity left precedence 130 } infix operator ∉ { associativity left precedence 130 } infix operator ⊂ { associativity left precedence 130 } infix operator ⊄ { associativity left precedence 130 } infix operator ⊆ { associativity left precedence 130 } infix operator ⊈ { associativity left precedence 130 } infix operator ⊃ { associativity left precedence 130 } infix operator ⊅ { associativity left precedence 130 } infix operator ⊇ { associativity left precedence 130 } infix operator ⊉ { associativity left precedence 130 }

Slide 16

Slide 16 text

ATTRIBUTES

Slide 17

Slide 17 text

@nonobjc @available @noreturn @noescape @autoclosure

Slide 18

Slide 18 text

@NONOBJC "Apply this attribute to a method, property, subscript, or initializer declaration to suppress an implicit objc attribute."

Slide 19

Slide 19 text

CUSTOM SUBSCRIPTS ON NSUserDefaults

Slide 20

Slide 20 text

extension NSUserDefaults { subscript(key: String) -> AnyObject? { get { return objectForKey(key) } set { setObject(newValue, forKey: key) } } subscript(key: String) -> Bool { // X - Subscript getter with Objective-C selector `objectForKeyedSubscript:` conflicts with previous declaration get { return boolForKey(key) } // X - Subscript setter with Objective-C selector `setObject:forKeyedSubscript:` conflicts with previous declaration set { setBool(newValue, forKey: key) } } // etc. }

Slide 21

Slide 21 text

SWIFT SUPPORTS RETURN TYPE POLYMORPHISM, OBJECTIVE-C DOESN'T. @NONOBJC TO THE RESCUE! !

Slide 22

Slide 22 text

extension NSUserDefaults { @nonobjc subscript(key: String) -> AnyObject? { get { return objectForKey(key) } set { setObject(newValue, forKey: key) } } @nonobjc subscript(key: String) -> Bool { get { return boolForKey(key) } set { setBool(newValue, forKey: key) } } // etc. }

Slide 23

Slide 23 text

@available "Apply this attribute to any declaration to indicate the declaration’s lifecycle relative to certain platforms and operating system versions."

Slide 24

Slide 24 text

* iOS OSX watchOS tvOS iOSApplicationExtension OSXApplicationExtension watchOSApplicationExtension tvOSApplicationExtension

Slide 25

Slide 25 text

class SampleViewController: UIViewController { init() { super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }

Slide 26

Slide 26 text

@noreturn @autoclosure

Slide 27

Slide 27 text

@noreturn public func fatalError(@autoclosure message: () -> String = default, file: StaticString = #file, line: UInt = #line) fatalError("ayy lmao")

Slide 28

Slide 28 text

@noescape

Slide 29

Slide 29 text

// Credit to @pearapps extension Optional { func iff(@noescape f: Wrapped -> Void) { switch self { case let .Some(value): f(value) default: return } } } let foo: String? = nil func bar(a: String) { print(a) } if let foo = foo { bar(foo) } foo.iff(bar)

Slide 30

Slide 30 text

METATYPES

Slide 31

Slide 31 text

"A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types."

Slide 32

Slide 32 text

protocol Reusable: class { static var reuseIdentifier: String { get } } extension UITableView { func registerReusable(cellClass: T.Type) { registerClass(cellClass, forCellReuseIdentifier: cellClass.reuseIdentifier) } func dequeueReusable(cellClass: T.Type) -> T { guard let cell = dequeueReusableCellWithIdentifier(cellClass.reuseIdentifier) as? T else { fatalError("Misconfigured cell type, \(cellClass)!") } return cell } // ... }

Slide 33

Slide 33 text

class CustomCell: UITableViewCell, Reusable { static let reuseIdentifier = "customCell" } class SomeTableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() tableView.registerReusable(CustomCell.self) } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { return tableView.dequeueReusable(CustomCell.self) } // ... }

Slide 34

Slide 34 text

Custom Pattern Matching

Slide 35

Slide 35 text

infix operator ~= { associativity none precedence 130 } Standard Library implementations

Slide 36

Slide 36 text

func ~=(pattern: T -> Bool, value: T) -> Bool { return pattern(value) } func isEven(a: T) -> Bool { return a % 2 == 0 } let x = 3 switch x { case isEven: print("even") default: print("odd") } @olebegemann's post on pattern matching

Slide 37

Slide 37 text

SWIFT PRANK 1 func ~=(_: T, _: U) -> Bool { return true } 1 Don't do this

Slide 38

Slide 38 text

fin @jasdev @PublicExtension