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

App & Service

App & Service

Ben Ragheb talked about two techniques he combined to implement a photo upload feature in his iOS app: (1) WebView/UIWebView JavaScript tricks, and (2) using Amazon Web Services.

CocoaHeadsNYC

May 09, 2013
Tweet

More Decks by CocoaHeadsNYC

Other Decks in Programming

Transcript

  1. In cloud computing the people are served by two separate,

    yet equally important parts. The applications which interact with the users and the services which process their data. These are their stories.
  2. Make Setup Easier • User snaps photo of blank log

    sheet • A sophisticated neural network (a.k.a. “Mike”) interprets photo • Send email to user when we’re done
  3. Amazon SNS • You create a topic • Use HTTP

    to publish something to the topic • Amazon forwards the something to the topic’s subscribers
  4. Amazon SNS • Message is up to 64KB of whatever

    you like • Can forward to email, text message, HTTP, or SQS
  5. Separation of Concerns • App doesn’t need to know where

    message is going • Amazon keeps track of subscriptions • Official Objective-C SDK available • I can procrastinate! • I hate writing web services
  6. Workflow • Read instructions • Take photo • Answer questions

    • Submit to service • Photo to S3 • Metadata to SNS
  7. Lingering Worry • AWS credentials baked into app • What

    if a malicious user extracts them? • New credentials requires App Store review • A token-vending machine is server-side code • I hate writing web services
  8. Let’s work on the app • Let’s use a web

    view to present instructions • Why not load content from the network? • Feature requires network access anyway • Now we can revise any time • “Out of Order”
  9. Buttons in HTML • Links that look like buttons •

    URL like “action:///takePhoto” • To disable feature, remove buttons
  10. - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSURL *URL =

    [request URL]; if ([[URL scheme] isEqualToString:@"action"]) { if ([[URL path] isEqualToString:@"/takePhoto"]) { [self takePhoto:nil]; return NO; } else if ([[URL path] isEqualToString:@"/choosePhoto"]) { [self choosePhoto:nil]; return NO; } } if (navigationType == UIWebViewNavigationTypeLinkClicked) { [[UIApplication sharedApplication] openURL:URL]; return NO; } return YES; }
  11. Serendipity • If I can transmit information to the user…

    • …surely I can transmit to my code • Use Javascript to embed metadata
  12. <script type="text/javascript"> var access = { key: 'AKIAJV3BLV6Z3Y7FKURQ', secret: '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',

    bucket: 'logbook-capture', topic: 'arn:aws:sns:us-east-1:693923277227:logbooks-topic' } var jpegQuality = 0.5; var facilityTypes = ["", "Commercial", "Healthcare", "Hotel", "Industrial/Manufacturing", "Maritime", "Multifamily Residential", "Power Plant/Generation", "University/School", "Warehouse", "Other"]; </script>
  13. • “document.body.id” to see if this is the page you

    want • “javascriptVariableName” to read a scalar value • “javascriptArrayName.join(‘\0’)” to read an array stringByEvaluatingJavaScriptFromString:
  14. - (void)webViewDidFinishLoad:(UIWebView *)webView { NSString *bodyID = [webView stringByEvaluatingJavaScriptFromString:@"document.body.id"]; if

    ([bodyID isEqualToString:@"start"]) { SGLogbookCaptureSession *session = [[SGLogbookCaptureSession alloc] init]; session.amazonAccessKey = [webView stringByEvaluatingJavaScriptFromString:@"access.key session.amazonSecretKey = [webView stringByEvaluatingJavaScriptFromString:@"access.sec session.amazonBucketName = [webView stringByEvaluatingJavaScriptFromString:@"access.bu session.amazonTopicARN = [webView stringByEvaluatingJavaScriptFromString:@"access.topi session.logbookImageQuality = [[webView stringByEvaluatingJavaScriptFromString:@"jpegQ NSString *facilityTypes = [webView stringByEvaluatingJavaScriptFromString:@"facilityTy session.facilityTypes = [facilityTypes componentsSeparatedByString:@"\0"]; SGAccountController *accountController = [SGAccountController sharedController]; if (![accountController isGuest]) { session.emailAddress = accountController.emailAddress; } self.session = session; } }
  15. Success • Lines of server code written: 0 • Token

    vending machine? Do it later! • Submission handler? Do it later! • I hate writing web services