Christina Moulton, Teak Mobile Inc.
Session 1: Design Patterns & Obj-C Features, Apple's Tools, SDK & Core Classes
Exercise 1: Interface Builder, IBOutlets & IBActions
Lunch
Session 2: iOS App Ecosystem, 3rd Party Tools & Testing, Key Resources
Exercise 2: JSON REST APIs, Storyboards & Tableviews
Session 3: Code Signing, App Store Submission, What Should Be a Native App vs. Web App
iOS App Ecosystem
3rd Party Tools & Testing
Key Resources
Devices, capabilities, resolutions
SDKs
Supporting older devices
Developer Portal
App Store & iTunes Connect
Hardware by capabilities iOS Device Compatibility (Apple)
If not key feature, check for presence & degrade gracefully (e.g., select photo vs. take photo)
SDK | iPhone | iPad | iPod Touch |
---|---|---|---|
7 | 4, 4S, 5, 5C, 5S (June 2010) | 2nd, 3rd, 4th, Mini, Mini Retina (March 2011) | 5th (Sept 2012) |
6.1.3 | 3GS (June 2009) | 4th (Sept 2010) | |
5.1.1 | 1st (April 2010) | 3rd (Sept 2009) | |
4.2.1 | 3G (July 2008) | 2nd (Sept 2008) | |
3.1.3 | 1st (June 2007) | 1st (Sept 2007) |
Check headers / docs or Deploymate
Weak link frameworks (mark as optional)
Check for support at run time
NSString *osVersion = [[UIDevice currentDevice] systemVersion];
if ([anObject respondsToSelector:@selector(aMethod)]) {
[anObject aMethod];
} else {
//Fail gracefully
}
// iOS 4.2+
if ([AClass class]) {
// Safe to use AClass
}
Links to:
Guidelines, checklists, marketing resources
SDK & Xcode downloads (pre-release)
Documentation, videos & demo code
Human Interface Guidelines (HIG)
Beta Testing & Crash Reporting: TestFlight, HockeyApp, Crashlytics
In-App Analytics: Flurry
Sales Analytics: App Annie, Distimo
3rd Party Framework Demo
iOS 3rd Party Tools: BaaS
iOS 3rd Party Libraries & Dependency Management: CocoaPods, AFNetworking, RESTKit
Symbolication (.dSYM): Keep your beta & App Store builds!
Download
Add framework
Import header
Integrate
Customize
Parse, Kinvey, StackMob, Appcelerator Cloud
Provide server-based functionality:
Database shared between users
Social login / user accounts
File storage
Push notifications
Custom server code
Scalability
Dependency Management: CocoaPods
RESTful Web Service: RESTKit
Networking Framework: AFNetworking
Custom UI Controls: Cocoa Controls
Lots of Frameworks: iOS Frameworks
JSON REST APIs, Storyboards & Tableviews
Install & import RESTKit
Create a Session Model Class
Load sessions
Add a tableview in the storyboard
Show the sessions as cells in the storyboard
Tapping on a session should show a larger view of the session with details
Want to try a lower level exercise? Connecting to webservice (RayWenderlich.com)
First create an empty project
In Xcode preferences menu, go to Downloads then Components. Install or update the Command Line tools
$ sudo gem update --system
$ [sudo] gem install cocoapods
$ pod setup
// Add Podfile to project: In Terminal
$ cd /path/to/MyProject
$ touch Podfile
$ edit Podfile
platform :ios, '6.0' // or '7.0'
pod 'RestKit', '~> 0.20.0'
$ pod install
// Now always use the workspace, not the project
$ open MyProject.xcworkspace
#import <RestKit/RestKit.h>
Add required libraries: SystemConfiguration & MobileCoreServices
@interface RKConferenceSession : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sessionDescription;
@property (nonatomic, copy) NSString *start_time;
@property (nonatomic, copy) NSString *end_time;
@property (nonatomic, copy) NSString *type;
@end
RKObjectMapping *mapping = [RKObjectMapping
mappingForClass:[RKConferenceSession class]];
[mapping addAttributeMappingsFromDictionary:
@{@"name": @"name", @"description":@"sessionDescription",
@"start_time":@"start_time", @"end_time":@"end_time",
@"type":@"type",}];
RKResponseDescriptor *descriptor =
[RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodAny pathPattern:nil
keyPath:@"objects" statusCodes:nil];
NSURL *url = [NSURL URLWithString:
@"http://api.go-opendata.ca/schedule/?format=json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
RKObjectRequestOperation *operation =
[[RKObjectRequestOperation alloc]
initWithRequest:request responseDescriptors:@[descriptor]];
[operation setCompletionBlockWithSuccess:
^(RKObjectRequestOperation *operation, RKMappingResult *result)
{
NSLog(@"The sessions are: %@", [result array]);
self.sessions = [result array];
} failure:^(RKObjectRequestOperation *operation, NSError *error)
{
NSLog(@"error: %@", error);
self.sessions = nil;
}];
[operation start];
Drag & drop
Create a custom UITableViewController subclass & assign it in the storyboard
Add a prototype cell in the storyboard, select the detail style
Add a property to the tableview to hold the sessions
@interface SessionsTableViewController : UITableViewController
@property (nonatomic, retain) NSArray *sessions;
@end
Move the loading code into the tableview and assign the results to the property when loading is done
- (void)viewDidLoad
{
[super viewDidLoad];
RKObjectMapping *mapping = ...
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result)
{
NSLog(@"The sessions are: %@", [result array]);
self.sessions = [result array];
[self.tableView reloadData];
} failure:^(RKObjectRequestOperation *operation, NSError *error)
{
NSLog(@"error: %@", error);
self.sessions = nil;
[self.tableView reloadData];
}];
[operation start];
}
Implement data source & delegate methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.sessions != nil)
{
return self.sessions.count;
}
return 0;
}
Implement data source & delegate methods:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"SessionCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
RKConferenceSession *session = self.sessions[indexPath.row];
cell.textLabel.text = session.name;
cell.detailTextLabel.text = session.start_time;
return cell;
}
In the storyboard, add a detail view with labels to show the details (set lines = 0 for description so it'll auto-resize)
Add a custom UIViewController subclass with IBOutlet properties for each label and a property to hold the Session Model Object
Hook up the outlets in the storyboard
Embed the tableview in a navigation controller
Right-click on the tableview prototype cell and drag a connection to the detail view. Choose "Selection Segue: Push"
Now we see the detail view, but the details aren't being set
In the tableview controller, we need to set the properties in prepareForSegue:
#import "SessionDetailViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Figure out which cell they tapped on
NSInteger selectionIndex =
[self.tableView indexPathForSelectedRow].row;
RKConferenceSession *session = self.sessions[selectionIndex];
// Get the new view controller
SessionDetailViewController *detailViewController =
(SessionDetailViewController *)[segue destinationViewController];
// Pass the selected object to the new view controller.
detailViewController.session = session;
}
Set all of the label text in the detail view:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.session)
{
self.nameLabel.text = self.session.name;
self.startTimeLabel.text = self.session.start_time;
self.endTimeLabel.text = self.session.end_time;
self.typeLabel.text = self.session.type;
self.descriptionLabel.text = self.session.sessionDescription;
}
}
Christina Moulton, Teak Mobile Inc.