Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Levelling Up with iOS 8 (PDX)

Levelling Up with iOS 8 (PDX)

This is the version of the talk presented at CocoaConf PDX in May 2015.

Ddd6d3bac7772fa67fc5e312a18bdaec?s=128

sammyd

May 09, 2015
Tweet

Transcript

  1. Levelling taking advantage of the lesser- known features of iOS

    8 Up github.com/sammyd/iOS8-LevellingUp
  2. hi, i’m sam @iwantmyrealname

  3. shinobicontrols.com/ios8cocoa

  4. shinobicontrols

  5. shinobicontrols shinobicontrols.com/giveaway

  6. iOS 8

  7. iOS 8

  8. we can’t all be headliners

  9. things broke

  10. today • Fixing the things that broke • Alerts /

    ActionSheets • Popovers • Notifications • CoreLocation • Playing with the unloved shiny things • AVKit • Notification actions • Rotation • Testing
  11. disclaimers too much code

  12. disclaimers too many topics

  13. disclaimers too many slides

  14. disclaimers rubbish story

  15. #winning

  16. fixing stuff

  17. notifications

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

  19. notifications Local No further authorisation required Remote func registerForRemoteNotifications() UIUserNotification

    Represents the appearance of all notifications (badge / alert / sound) func registerUserNotificationSettings(notificationSettings: UIUserNotificationSettings)
  20. notifications let requestedTypes: UIUserNotificationType = .Alert | .Sound | .Badge

    let settingsRequest = UIUserNotificationSettings(forTypes: requestedTypes, categories: nil) UIApplication.sharedApplication().registerUserNotificationSettings(sett ingsRequest)
  21. notifications

  22. notifications

  23. notifications UIApplication func currentUserNotificationSettings() -> UIUserNotificationSettings! UIApplicationDelegate optional func application(application:

    UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings)
  24. notifications demo

  25. alerts / action sheets

  26. alerts & action sheets

  27. 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!) ...
  28. 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
  29. 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)
  30. alerts & action sheets demo

  31. 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)
  32. popovers

  33. popovers

  34. popovers UIPopoverController init(contentViewController viewController: UIViewController) •Not adaptive •Presentation is now

    handled by UIPresentationController
  35. popovers UIModalPresentationStyle enum UIModalPresentationStyle : Int { case Popover ...

    } UIViewController var modalPresentationStyle: UIModalPresentationStyle var popoverPresentationController: UIPopoverPresentationController? { get }
  36. 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]! ... }
  37. popovers demo

  38. popovers demo let popoverVC = storyboard? .instantiateViewControllerWithIdentifier("PopoverContentVC") as! UIViewController popoverVC.modalPresentationStyle

    = .Popover presentViewController(popoverVC, animated: true, completion: nil) if let popoverController = popoverVC.popoverPresentationController { popoverController.sourceView = sender popoverController.sourceRect = sender.bounds popoverController.permittedArrowDirections = .Any popoverController.delegate = self }
  39. popovers

  40. core location

  41. core location CLLocationManager func requestAlwaysAuthorization() func requestWhenInUseAuthorization() class func authorizationStatus()

    -> CLAuthorizationStatus CLAuthorizationStatus enum CLAuthorizationStatus : Int32 { ... case AuthorizedAlways case AuthorizedWhenInUse }
  42. core location Always • Foreground and background • Able to

    launch the app • Geofencing / Region monitoring • Even if killed by user • Users will be asked to confirm “later” WhenInUse • Foreground only • Not allowed access to launch APIs • Banner displayed when app moves to background
  43. 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 }
  44. core location doesn’t work

  45. 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()
  46. 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()
  47. 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()
  48. core location demo

  49. core location

  50. core location

  51. new shiny

  52. settings link

  53. link to settings UIApplicationOpenSettingsURLString

  54. link to settings demo

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

  56. link to settings

  57. AVKit

  58. AVKit AVPlayerViewController class AVPlayerViewController : UIViewController { var player: AVPlayer!

    var showsPlaybackControls: Bool var videoGravity: String! ... }
  59. AVKit demo

  60. 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() } }
  61. AVKit

  62. notification actions

  63. notification actions

  64. 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 } }
  65. notification actions UIMutableUserNotificationAction class UIMutableUserNotificationAction : UIUserNotificationAction { var identifier:

    String! var title: String! var activationMode: UIUserNotificationActivationMode var authenticationRequired: Bool var destructive: Bool }
  66. 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) }
  67. notification actions UIUserNotificationSettings class UIUserNotificationSettings : NSObject { convenience init(forTypes

    types: UIUserNotificationType, categories: Set<NSObject>?) var types: UIUserNotificationType { get } var categories: Set<NSObject>! { get } }
  68. notification actions demo

  69. 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)
  70. 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() }
  71. notification actions

  72. 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", } }
  73. notification actions

  74. rotation

  75. rotation extension UIViewController { ... var interfaceOrientation: UIInterfaceOrientation { get

    } func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) }
  76. 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) }
  77. 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) }
  78. rotation protocol UIContentContainer : NSObjectProtocol { ... func viewWillTransitionToSize(size: CGSize,

    withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) }
  79. 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) }
  80. rotation UIViewControllerTransisionCoordinatorContext protocol UIViewControllerTransitionCoordinatorContext : NSObjectProtocol { ... @availability(iOS, introduced=8.0)

    func targetTransform() -> CGAffineTransform }
  81. rotation demo

  82. 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 ... }) } }
  83. 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) }) } }
  84. async testing

  85. 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()}) }) }
  86. 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! ... }
  87. async testing demo

  88. 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) }
  89. 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 }
  90. 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) }
  91. performance tests

  92. test performance class XCTestCase : XCTest { ... func measureBlock(block:

    (() -> Void)!) }
  93. test performance demo

  94. 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) } }
  95. test performance

  96. test performance

  97. where next?

  98. 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
  99. where next? shinobicontrols.com/ios8cocoa

  100. where next? nshipster.com asciiwwdc.com

  101. invest time

  102. questions?

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