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

Working with web APIs in Swift

Working with web APIs in Swift

Twilio Signal, May 19, 2015
https://www.youtube.com/watch?v=ISyve2_dj6c

Swift is Apple's hot new programming language for building iOS and OS X apps. This talk will demonstrate an example app in Swift that connects to the Dropbox API. We'll explore topics that apply to working with any API, including best practices for making snappy and resilient requests as well as handling JSON success and error responses.

Leah Culver

May 19, 2015
Tweet

More Decks by Leah Culver

Other Decks in Programming

Transcript

  1. API v2 preview • HTTP POST method • JSON in

    the request and response body • fewer HTTP status codes • more descriptive error messages • general cleanup ✨
  2. API v2 preview endpoints User endpoints users/get_account users/get_current_account File endpoints

    files/get_metadata files/list_folder files/upload files/download files/search … and more!
  3. SwiftyDropbox SDK (v0.1) methods User endpoints usersGetAccount usersGetCurrentAccount File endpoints

    filesGetMetadata filesListFolder filesUpload filesDownload filesSearch … and more!
  4. =

  5. Link account with Dropbox func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject:

    AnyObject]?) -> Bool { DropboxAuthManager.sharedAuthManager = DropboxAuthManager(appKey: “APP_KEY”) return true } AppDelegate.swift
  6. @IBAction func linkButtonPressed(sender: AnyObject) { // Present view to log

    in DropboxAuthManager.sharedAuthManager.authorizeFromController(self) } ViewController.swift
  7. OAuth 2 on iOS • switch to Safari for log

    in and authorization • present web view controller
  8. OAuth 2 on iOS • switch to Safari for log

    in and authorization • present web view controller
  9. OAuth 2 on iOS • switch to Safari for log

    in and authorization • present web view controller • switch to Dropbox app (“DAuth”, not yet implemented)
  10. func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?)

    -> Bool { if let authResult = DropboxAuthManager.sharedAuthManager.handleRedirectURL(url) { switch authResult { case .Success(let token): println("Success! User is logged into Dropbox.") case .Error(let error, let description): println("Error: \(description)") } } return false } AppDelegate.swift
  11. Set up DropboxClient // Check if the user is logged

    in if let token = DropboxAuthManager.sharedAuthManager.getFirstAccessToken() { // Set the sharedClient for re-use DropboxClient.sharedClient = DropboxClient(accessToken: token) … } ViewController.swift
  12. List user’s Dropbox files self.filenames = [] // List contents

    of app folder DropboxClient.sharedClient.filesListFolder(path: "").response { response, error in if let result = response { for entry in result.entries { // Check that file is a photo (by file extension) if fileURL.absoluteString!.hasSuffix(".jpg") || fileURL.absoluteString!.hasSuffix(".png") { // Add photo self.filenames?.append(entry.name) } } } } ViewController.swift
  13. UIPageViewControllerDataSource func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {

    var nextIndex = … let photoViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PhotoViewController") as! PhotoViewController photoViewController.filename = self.filenames![nextIndex] return photoViewController }
  14. Download photo class PhotoViewController: UIViewController { @IBOutlet weak var imageView:

    UIImageView! var filename: String? override func viewDidLoad() { … // Download the photo from Dropbox // A thumbnail would be better but there's no endpoint for that in API v2 yet! DropboxClient.sharedClient.filesDownload(path: “/\(self.filename)").response { response, error in if let (metadata, data) = response, image = UIImage(data: data) { // Resize image for watch (so it's not huge) let resizedImage = self.resizeImage(image) // Display image self.imageView.image = resizedImage } } } }
  15. Store photos in app group // Get app group shared

    by phone and watch let containerURL = NSFileManager.defaultManager() .containerURLForSecurityApplicationGroupIdentifier("group.com.Dropbox.DropboxPhotoWatch") if let fileURL = containerURL?.URLByAppendingPathComponent(filename) { if let data = NSData(contentsOfURL: fileURL) { // Display image self.imageView.image = UIImage(data: data) } else { // Download the photo from Dropbox, resize, then display … self.imageView.image = resizedImage // Save image to local filesystem app group - allows us to access in the watch let resizedImageData = UIImageJPEGRepresentation(resizedImage, 1.0) resizedImageData.writeToURL(fileURL, atomically: true) } }
  16. InterfaceController.swift override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Get images

    from the shared app group if let images = self.getImagesFromAppGroup() { // Data for each photo view let controllerNames = [String](count: images.count, repeatedValue: "PhotoInterfaceController") let contexts = images.map({ image in ["image": image] }) // Update watch display WKInterfaceController.reloadRootControllersWithNames(controllerNames, contexts: contexts) } }
  17. InterfaceController.swift private func getImagesFromAppGroup() -> Array<UIImage>? { var images =

    [UIImage]() // Get app group shared by phone and watch if let containerURL = NSFileManager.defaultManager() .containerURLForSecurityApplicationGroupIdentifier("group.com.Dropbox.DropboxPhotoWatch") { // Fetch all files in the app group for fileURL in NSFileManager.defaultManager().contentsOfDirectoryAtURL(…) as! Array <NSURL> { // Check that file is a photo (by file extension) if fileURL.absoluteString!.hasSuffix(".jpg") || fileURL.absoluteString!.hasSuffix(".png") { // Add image to array of images if let data = NSData(contentsOfURL: fileURL), image = UIImage(data: data) { images.append(image) } } } return images }
  18. PhotoInterfaceController.swift class PhotoInterfaceController: WKInterfaceController { @IBOutlet weak var image: WKInterfaceImage!

    override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) if let data = context as? Dictionary<String, UIImage> { self.image.setImage(data["image"]) } } }
  19. Optionals if let (metadata, data) = response { // …

    } else { println("Error: \(error!)") }
  20. Optional chaining (new!) if let (metadata, data) = response, image

    = UIImage(data: data) { // Display image self.imageView.image = image } else { println("Error downloading file from Dropbox: \(error!)") }
  21. iOS app longer lifetime of in-memory code Web app request/response

    pattern API clients DropboxClient DropboxAuthManager
  22. + (id)sharedManager { static MyManager *sharedManager = nil; static dispatch_once_t

    onceToken; dispatch_once(&onceToken, ^{ sharedManager = [[self alloc] init]; }); return sharedManager; } class MyManager { static let sharedManager = MyManager() init() { //... } } Singletons Obj-C Swift
  23. Enumerations if let authResult = DropboxAuthManager.sharedAuthManager.handleRedirectURL(url) { switch authResult {

    case .Success(let token): println("Success! User is logged into Dropbox.") case .Error(let error, let description): println("Error: \(description)") } }
  24. public enum DropboxAuthResult { case Success(DropboxAccessToken) case Error(OAuth2Error, String) }

    public func handleRedirectURL(url: NSURL) -> DropboxAuthResult? { … } Enumerations
  25. DropboxClient.sharedClient.filesListFolder(path: "").response { response, error in if response != nil

    { println(response) } else if error != nil { println("Error: \(error)") } } Completion handler