Slide 1

Slide 1 text

Levelling taking advantage of the lesser- known features of iOS 8 Up github.com/sammyd/iOS8-LevellingUp

Slide 2

Slide 2 text

hi, i’m sam @iwantmyrealname

Slide 3

Slide 3 text

shinobicontrols.com/ios8cocoa

Slide 4

Slide 4 text

shinobicontrols

Slide 5

Slide 5 text

shinobicontrols

Slide 6

Slide 6 text

iOS 8

Slide 7

Slide 7 text

iOS 8

Slide 8

Slide 8 text

we can’t all be headliners

Slide 9

Slide 9 text

things broke

Slide 10

Slide 10 text

today • Fixing the things that broke • Alerts / ActionSheets • Popovers • Notifications • CoreLocation • Playing with the unloved shiny things • AVKit • Notification actions • Rotation • Testing

Slide 11

Slide 11 text

disclaimers too much code

Slide 12

Slide 12 text

disclaimers too many topics

Slide 13

Slide 13 text

disclaimers too many slides

Slide 14

Slide 14 text

disclaimers rubbish story

Slide 15

Slide 15 text

#winning

Slide 16

Slide 16 text

fixing stuff

Slide 17

Slide 17 text

notifications

Slide 18

Slide 18 text

notifications Local No authorisation required Remote func registerForRemoteNotificationTypes (types: UIRemoteNotificationType)

Slide 19

Slide 19 text

notifications Local No further authorisation required Remote func registerForRemoteNotifications() UIUserNotification Represents the appearance of all notifications (badge / alert / sound) func registerUserNotificationSettings(notificationSettings: UIUserNotificationSettings)

Slide 20

Slide 20 text

notifications let requestedTypes: UIUserNotificationType = .Alert | .Sound | .Badge let settingsRequest = UIUserNotificationSettings(forTypes: requestedTypes, categories: nil) UIApplication.sharedApplication().registerUserNotificationSettings(sett ingsRequest)

Slide 21

Slide 21 text

notifications

Slide 22

Slide 22 text

notifications

Slide 23

Slide 23 text

notifications UIApplication func currentUserNotificationSettings() -> UIUserNotificationSettings! UIApplicationDelegate optional func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings)

Slide 24

Slide 24 text

notifications demo

Slide 25

Slide 25 text

alerts / action sheets

Slide 26

Slide 26 text

alerts & action sheets

Slide 27

Slide 27 text

alerts & action sheets UIAlertView init(title: String?, message: String?, delegate: AnyObject?, cancelButtonTitle: String?) func show() UIActionSheet init(title: String?, delegate: UIActionSheetDelegate?, cancelButtonTitle: String?, destructiveButtonTitle: String?) func showFromToolbar(view: UIToolbar!) func showFromTabBar(view: UITabBar!) ...

Slide 28

Slide 28 text

alerts & action sheets UIAlertController class UIAlertController : UIViewController { convenience init(title: String?, message: String?, preferredStyle: UIAlertControllerStyle) ... } func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) Alerts UIAlertControllerStyle.Alert Action Sheet UIAlertControllerStyle.ActionSheet

Slide 29

Slide 29 text

alerts & action sheets UIAlertAction class UIAlertAction : NSObject, NSCopying { convenience init(title: String, style: UIAlertActionStyle, handler: ((UIAlertAction!) -> Void)!) ... } UIAlertActionStyle enum UIAlertActionStyle : Int { case Default case Cancel case Destructive } UIAlertController func addAction(action: UIAlertAction)

Slide 30

Slide 30 text

alerts & action sheets demo let alert = UIAlertController(title: “Alert", message: "The alert controller", preferredStyle: .Alert) let dismiss = { (action: UIAlertAction!) in self.dismissViewControllerAnimated(true, completion: nil) } alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: dismiss)) alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: dismiss)) alert.addTextFieldWithConfigurationHandler { textField in textField.placeholder = "Sample text field" } presentViewController(alert, animated: true, completion: nil)

Slide 31

Slide 31 text

alerts & action sheets demo

Slide 32

Slide 32 text

popovers

Slide 33

Slide 33 text

popovers

Slide 34

Slide 34 text

popovers UIPopoverController init(contentViewController viewController: UIViewController) •Not adaptive •Presentation is now handled by UIPresentationController

Slide 35

Slide 35 text

popovers UIModalPresentationStyle enum UIModalPresentationStyle : Int { case Popover ... } UIViewController var modalPresentationStyle: UIModalPresentationStyle var popoverPresentationController: UIPopoverPresentationController? { get }

Slide 36

Slide 36 text

popovers UIPopoverPresentationController class UIPopoverPresentationController : UIPresentationController { unowned(unsafe) var delegate: UIPopoverPresentationControllerDelegate? var permittedArrowDirections: UIPopoverArrowDirection var sourceView: UIView! var sourceRect: CGRect var barButtonItem: UIBarButtonItem! var passthroughViews: [AnyObject]! ... }

Slide 37

Slide 37 text

popovers demo let popoverVC = storyboard? .instantiateViewControllerWithIdentifier("PopoverContentVC") as! UIViewController popoverVC.modalPresentationStyle = .Popover if let popoverController = popoverVC.popoverPresentationController { popoverController.sourceView = sender popoverController.sourceRect = sender.bounds popoverController.permittedArrowDirections = .Any popoverController.delegate = self } presentViewController(popoverVC, animated: true, completion: nil)

Slide 38

Slide 38 text

popovers

Slide 39

Slide 39 text

popovers demo

Slide 40

Slide 40 text

core location

Slide 41

Slide 41 text

core location CLLocationManager func requestAlwaysAuthorization() func requestWhenInUseAuthorization() class func authorizationStatus() -> CLAuthorizationStatus CLAuthorizationStatus enum CLAuthorizationStatus : Int32 { ... case AuthorizedAlways case AuthorizedWhenInUse }

Slide 42

Slide 42 text

core location Always • Foreground and background • Able to launch the app • Geofencing / Region monitoring • Even if killed by user • Users will be asked to confim “later” WhenInUse • Foreground only • Not allowed access to launch APIs • Banner displayed when app moves to background •

Slide 43

Slide 43 text

core location CLLocationManager override func viewDidLoad() { super.viewDidLoad() // Prepare the location manager locationManager.delegate = self locationManager.desiredAccuracy = 20 // Need to ask for the right permissions locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() // Prepare the map view mapView.delegate = self }

Slide 44

Slide 44 text

core location doesn’t work

Slide 45

Slide 45 text

core location CLLocationManager /* * requestWhenInUseAuthorization * * Discussion: * When +authorizationStatus == kCLAuthorizationStatusNotDetermined, * calling this method will trigger a prompt to request "when-in-use" * authorization from the user. If possible, perform this call in response * to direct user request for a location-based service so that the reason * for the prompt will be clear. Any authorization change as a result of * the prompt will be reflected via the usual delegate callback: * -locationManager:didChangeAuthorizationStatus:. * * If received, "when-in-use" authorization grants access to the user's * location via -startUpdatingLocation/-startRangingBeaconsInRegion while * in the foreground. If updates have been started when going to the * background, then a status bar banner will be displayed to maintain * visibility to the user, and updates will continue until stopped * normally, or the app is killed by the user. * * "When-in-use" authorization does NOT enable monitoring API on regions, * significant location changes, or visits, and -startUpdatingLocation will * not succeed if invoked from the background. * * When +authorizationStatus != kCLAuthorizationStatusNotDetermined, (ie * generally after the first call) this method will do nothing. * * If the NSLocationWhenInUseUsageDescription key is not specified in your * Info.plist, this method will do nothing, as your app will be assumed not * to support WhenInUse authorization. */ @availability(iOS, introduced=8.0) func requestWhenInUseAuthorization()

Slide 46

Slide 46 text

core location CLLocationManager /* * requestWhenInUseAuthorization * * Discussion: * When +authorizationStatus == kCLAuthorizationStatusNotDetermined, * calling this method will trigger a prompt to request "when-in-use" * authorization from the user. If possible, perform this call in response * to direct user request for a location-based service so that the reason * for the prompt will be clear. Any authorization change as a result of * the prompt will be reflected via the usual delegate callback: * -locationManager:didChangeAuthorizationStatus:. * * If received, "when-in-use" authorization grants access to the user's * location via -startUpdatingLocation/-startRangingBeaconsInRegion while * in the foreground. If updates have been started when going to the * background, then a status bar banner will be displayed to maintain * visibility to the user, and updates will continue until stopped * normally, or the app is killed by the user. * * "When-in-use" authorization does NOT enable monitoring API on regions, * significant location changes, or visits, and -startUpdatingLocation will * not succeed if invoked from the background. * * When +authorizationStatus != kCLAuthorizationStatusNotDetermined, (ie * generally after the first call) this method will do nothing. * * If the NSLocationWhenInUseUsageDescription key is not specified in your * Info.plist, this method will do nothing, as your app will be assumed not * to support WhenInUse authorization. */ @availability(iOS, introduced=8.0) func requestWhenInUseAuthorization()

Slide 47

Slide 47 text

core location CLLocationManager * If the NSLocationWhenInUseUsageDescription key is not * specified in your Info.plist, this method will do nothing, * as your app will be assumed not to support WhenInUse * authorization. */ @availability(iOS, introduced=8.0) func requestWhenInUseAuthorization()

Slide 48

Slide 48 text

core location

Slide 49

Slide 49 text

core location

Slide 50

Slide 50 text

core location demo

Slide 51

Slide 51 text

new shiny

Slide 52

Slide 52 text

settings link

Slide 53

Slide 53 text

link to settings UIApplicationOpenSettingsURLString

Slide 54

Slide 54 text

link to settings let settingsUrl = NSURL(string: UIApplicationOpenSettingsURLString) UIApplication.sharedApplication().openURL(settingsUrl!)

Slide 55

Slide 55 text

link to settings

Slide 56

Slide 56 text

link to settings demo

Slide 57

Slide 57 text

AVKit

Slide 58

Slide 58 text

AVKit AVPlayerViewController class AVPlayerViewController : UIViewController { var player: AVPlayer! var showsPlaybackControls: Bool var videoGravity: String! ... }

Slide 59

Slide 59 text

AVKit class AVKitViewController: AVPlayerViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. let url = NSBundle.mainBundle().URLForResource("countdown_new", withExtension: "mov") player = AVPlayer(URL: url) // Some configuration showsPlaybackControls = true // AVFoundation pipeline controls player.play() } }

Slide 60

Slide 60 text

AVKit

Slide 61

Slide 61 text

AVKit demo

Slide 62

Slide 62 text

notification actions

Slide 63

Slide 63 text

notification actions

Slide 64

Slide 64 text

notification actions UIUserNotificationAction class UIUserNotificationAction : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding { var identifier: String! { get } var title: String! { get } var activationMode: UIUserNotificationActivationMode { get } var authenticationRequired: Bool { get } var destructive: Bool { get } }

Slide 65

Slide 65 text

notification actions UIMutableUserNotificationAction class UIMutableUserNotificationAction : UIUserNotificationAction { var identifier: String! var title: String! var activationMode: UIUserNotificationActivationMode var authenticationRequired: Bool var destructive: Bool }

Slide 66

Slide 66 text

notification actions UIUserNotificationCategory class UIUserNotificationCategory : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding { var identifier: String! { get } func actionsForContext(context: UIUserNotificationActionContext) -> [AnyObject]! } UIMutableUserNotificationCategory class UIMutableUserNotificationCategory : UIUserNotificationCategory { var identifier: String! func setActions(actions: [AnyObject]!, forContext context: UIUserNotificationActionContext) }

Slide 67

Slide 67 text

notification actions UIUserNotificationSettings class UIUserNotificationSettings : NSObject { convenience init(forTypes types: UIUserNotificationType, categories: Set?) var types: UIUserNotificationType { get } var categories: Set! { get } }

Slide 68

Slide 68 text

notification actions let cancelAction = UIMutableUserNotificationAction() cancelAction.identifier = cancelNotificationString cancelAction.destructive = true cancelAction.title = "Cancel" cancelAction.activationMode = .Background cancelAction.authenticationRequired = false ... let category = UIMutableUserNotificationCategory() category.identifier = notificationActionDemoString category.setActions([cancelAction, askAgainAction], forContext: .Minimal) let requestedTypes = UIUserNotificationType.Alert let settingsRequest = UIUserNotificationSettings(forTypes: requestedTypes, categories: [category]) UIApplication.sharedApplication() .registerUserNotificationSettings(settingsRequest)

Slide 69

Slide 69 text

notification actions UIApplicationDelegate func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) { if let identifier = identifier { switch identifier { case cancelNotificationString: println("You cancelled") case askAgainNotificationString: println("You restarted!") default: break } } completionHandler() }

Slide 70

Slide 70 text

notification actions Local let notification = UILocalNotification() notification.fireDate = NSDate(timeIntervalSinceNow: 3) notification.alertBody = "Asking you now" notification.category = notificationActionDemoString UIApplication.sharedApplication() .scheduleLocalNotification(notification) Remote { "aps" : { "alert" : "You’re invited!", "category" : "AskMeNotificationString", } }

Slide 71

Slide 71 text

notification actions

Slide 72

Slide 72 text

notification actions

Slide 73

Slide 73 text

notification actions demo

Slide 74

Slide 74 text

rotation

Slide 75

Slide 75 text

rotation extension UIViewController { ... var interfaceOrientation: UIInterfaceOrientation { get } func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) }

Slide 76

Slide 76 text

rotation extension UIViewController { ... @availability(iOS, introduced=2.0, deprecated=8.0) var interfaceOrientation: UIInterfaceOrientation { get } @availability(iOS, introduced=2.0, deprecated=8.0, message="Implement viewWillTransitionToSize:withTransitionCoordinator: instead") func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) @availability(iOS, introduced=2.0, deprecated=8.0) func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) @availability(iOS, introduced=3.0, deprecated=8.0, message="Implement viewWillTransitionToSize:withTransitionCoordinator: instead") func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) }

Slide 77

Slide 77 text

rotation extension UIViewController { ... @availability(iOS, introduced=2.0, deprecated=8.0) var interfaceOrientation: UIInterfaceOrientation { get } @availability(iOS, introduced=2.0, deprecated=8.0, message="Implement viewWillTransitionToSize:withTransitionCoordinator: instead") func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) @availability(iOS, introduced=2.0, deprecated=8.0) func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) @availability(iOS, introduced=3.0, deprecated=8.0, message="Implement viewWillTransitionToSize:withTransitionCoordinator: instead") func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) }

Slide 78

Slide 78 text

rotation extension UIViewController { ... @availability(iOS, introduced=2.0, deprecated=8.0) var interfaceOrientation: UIInterfaceOrientation { get } @availability(iOS, introduced=2.0, deprecated=8.0, message="Implement viewWillTransitionToSize:withTransitionCoordinator: instead") func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) @availability(iOS, introduced=2.0, deprecated=8.0) func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) @availability(iOS, introduced=3.0, deprecated=8.0, message="Implement viewWillTransitionToSize:withTransitionCoordinator: instead") func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) }

Slide 79

Slide 79 text

rotation protocol UIContentContainer : NSObjectProtocol { ... func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) }

Slide 80

Slide 80 text

rotation UIViewControllerTransitionCoordinator protocol UIViewControllerTransitionCoordinator : UIViewControllerTransitionCoordinatorContext, NSObjectProtocol { func animateAlongsideTransition( animation: ((UIViewControllerTransitionCoordinatorContext!) -> Void)!, completion: ((UIViewControllerTransitionCoordinatorContext!) -> Void)!) -> Bool func animateAlongsideTransitionInView(view: UIView!, animation: ((UIViewControllerTransitionCoordinatorContext!) -> Void)!, completion: ((UIViewControllerTransitionCoordinatorContext!) -> Void)!) -> Bool func notifyWhenInteractionEndsUsingBlock( handler: (UIViewControllerTransitionCoordinatorContext!) -> Void) }

Slide 81

Slide 81 text

rotation UIViewControllerTransisionCoordinatorContext protocol UIViewControllerTransitionCoordinatorContext : NSObjectProtocol { ... @availability(iOS, introduced=8.0) func targetTransform() -> CGAffineTransform }

Slide 82

Slide 82 text

rotation override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator) let targetTransform = coordinator.targetTransform() if !CGAffineTransformIsIdentity(targetTransform) { coordinator.animateAlongsideTransition({ context in UIView.animateWithDuration(context.transitionDuration()) { for monkey in self.monkeyLabels { monkey.transform = CGAffineTransformInvert(targetTransform) } } }, completion: { context in ... }) } }

Slide 83

Slide 83 text

rotation override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { ... if !CGAffineTransformIsIdentity(targetTransform) { coordinator.animateAlongsideTransition({ ... }, completion: { context in UIView.animateWithDuration(1, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0, options: .allZeros, animations: { for monkey in self.monkeyLabels { monkey.transform = CGAffineTransformIdentity } }, completion: nil) }) } }

Slide 84

Slide 84 text

rotation demo

Slide 85

Slide 85 text

async testing

Slide 86

Slide 86 text

async testing public func someKindOfAsyncMethod(completionHandler: () -> ()) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0), { sleep(3) dispatch_async(dispatch_get_main_queue(), { completionHandler()}) }) }

Slide 87

Slide 87 text

async testing class XCTestExpectation : NSObject { func fulfill() } extension XCTestCase { func expectationWithDescription(description: String!) -> XCTestExpectation! func waitForExpectationsWithTimeout(timeout: NSTimeInterval, handler handlerOrNil: XCWaitCompletionHandler!) func keyValueObservingExpectationForObject(objectToObserve: AnyObject!, keyPath: String!, expectedValue: AnyObject!) -> XCTestExpectation! ... }

Slide 88

Slide 88 text

async testing func testBasicAsyncMethod() { // Check that we get called back as expected let expectation = expectationWithDescription("Async Method") raceController.someKindOfAsyncMethod({ expectation.fulfill() }) waitForExpectationsWithTimeout(5, handler: nil) }

Slide 89

Slide 89 text

async testing raceController.startRace(5, horseCrossedLineCallback:{ (horse:Horse) in // Deal with the number of horses self.numberOfHorsesCurrentlyRunning -= 1 if self.numberOfHorsesCurrentlyRunning == 0 { self.resetButton.enabled = true }

Slide 90

Slide 90 text

async testing func testResetButtonEnabledOnceRaceComplete() { let expectation = keyValueObservingExpectationForObject( viewController.resetButton, keyPath: “enabled", expectedValue: true) // Simulate tapping the start race button viewController.handleStartRaceButton(viewController.startRaceButton) // Wait for the test to run waitForExpectationsWithTimeout(5, handler: nil) }

Slide 91

Slide 91 text

async testing demo

Slide 92

Slide 92 text

performance tests

Slide 93

Slide 93 text

test performance class XCTestCase : XCTest { ... func measureBlock(block: (() -> Void)!) }

Slide 94

Slide 94 text

test performance func testMovingAveragePerformance() { // This is an example of a performance test case. calculator.windowSize = 1000 self.measureBlock() { // Put the code you want to measure the time of here. let randomArray = self.RandomDouble(10000) let result = self.calculator.calculateMovingAverage(randomArray) XCTAssertEqual(result.count, 9000) } }

Slide 95

Slide 95 text

test performance

Slide 96

Slide 96 text

test performance

Slide 97

Slide 97 text

test performance demo

Slide 98

Slide 98 text

where next?

Slide 99

Slide 99 text

where next? Custom fonts in Interface Builder Designated initialisers in Swift / ObjC Nullability annotations in ObjC New detectors in CoreImage NSFormatter Auto hiding of navigation/tab bars Custom CIFilter Kernels Live rendering in Interface Builder Layout margins CoreMotion Location notifications NSCalendar additions

Slide 100

Slide 100 text

where next? shinobicontrols.com/ios8cocoa

Slide 101

Slide 101 text

where next? nshipster.com asciiwwdc.com

Slide 102

Slide 102 text

invest time

Slide 103

Slide 103 text

questions?

Slide 104

Slide 104 text

questions? @iwantmyrealname github.com/sammyd/iOS8-LevellingUp speakerdeck.com/sammyd shinobicontrols.com/ios8cocoa