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

Levelling Up with iOS 8

sammyd
March 28, 2015

Levelling Up with iOS 8

iOS 8 was somewhat overshadowed by the introduction of a massive white bird in an orange sky. There were loads of cool new things in iOS 8, and this talk is a walk through some of the more obscure of these.

sammyd

March 28, 2015
Tweet

More Decks by sammyd

Other Decks in Programming

Transcript

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

    8 Up github.com/sammyd/iOS8-LevellingUp
  2. today • Fixing the things that broke • Alerts /

    ActionSheets • Popovers • Notifications • CoreLocation • Playing with the unloved shiny things • AVKit • Notification actions • Rotation • Testing
  3. notifications Local No further authorisation required Remote func registerForRemoteNotifications() UIUserNotification

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

    let settingsRequest = UIUserNotificationSettings(forTypes: requestedTypes, categories: nil) UIApplication.sharedApplication().registerUserNotificationSettings(sett ingsRequest)
  5. 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!) ...
  6. 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
  7. 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)
  8. 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)
  9. popovers UIModalPresentationStyle enum UIModalPresentationStyle : Int { case Popover ...

    } UIViewController var modalPresentationStyle: UIModalPresentationStyle var popoverPresentationController: UIPopoverPresentationController? { get }
  10. 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]! ... }
  11. 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)
  12. core location CLLocationManager func requestAlwaysAuthorization() func requestWhenInUseAuthorization() class func authorizationStatus()

    -> CLAuthorizationStatus CLAuthorizationStatus enum CLAuthorizationStatus : Int32 { ... case AuthorizedAlways case AuthorizedWhenInUse }
  13. 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 •
  14. 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 }
  15. 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()
  16. 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()
  17. 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()
  18. 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() } }
  19. 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 } }
  20. notification actions UIMutableUserNotificationAction class UIMutableUserNotificationAction : UIUserNotificationAction { var identifier:

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

    types: UIUserNotificationType, categories: Set<NSObject>?) var types: UIUserNotificationType { get } var categories: Set<NSObject>! { get } }
  23. 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)
  24. 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() }
  25. 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", } }
  26. rotation extension UIViewController { ... var interfaceOrientation: UIInterfaceOrientation { get

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

    withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) }
  31. 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) }
  32. 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 ... }) } }
  33. 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) }) } }
  34. 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! ... }
  35. 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) }
  36. 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 }
  37. 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) }
  38. 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) } }
  39. 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