SlideShare ist ein Scribd-Unternehmen logo
1 von 95
"Stop it! That
     Hurts!"
      Common iOS Anti-Patterns

@CarlBrwn                             Carl Brown

            Missing final vowel (blame Flickr)      2012
ObBio
Carl Brown (@CarlBrwn)
iOS Contractor
Teach Intro to iOS classes
Co-Organizer CocoaCoder.org
Started Mobile Programming 2004 (Palm)
Half of team (Nov. '08) that wrote
LIVESTRONG.com Calorie Tracker


                                         2012
ObBio
Carl Brown (@CarlBrwn)
iOS Contractor
Teach Intro to iOS classes
Co-Organizer CocoaCoder.org
Started Mobile Programming 2004 (Palm)
Half of team (Nov. '08) that wrote
LIVESTRONG.com Calorie Tracker


                                         2012
Why So Much Bad Code?




I live in Austin Texas
                         2012
Why So Much Bad Code?




"Looks like any idiot can make an App"
                                         2012
Why So Much Bad Code?




I try not to travel
                          2012
Why So Much Bad Code?

 Working with clients in Austin
 Where SxSW implies that
  any idiot can found App startup
 oDesk/Elance/etc are full of idiots
 2008 NDA gave idiots a running start



                                        2012
Not "Bad Code"?
   At least for purposes of this talk...
Braces for 'if' on next line




                                           2012
Not "Bad Code"?
   At least for purposes of this talk...
Braces for 'if' on next line
Singletons ([Class sharedInstance])




                                           2012
Not "Bad Code"?
   At least for purposes of this talk...
Braces for 'if' on next line
Singletons ([Class sharedInstance])
Dot Notation (self.foo vs [self foo])




                                           2012
Not "Bad Code"?
   At least for purposes of this talk...
Braces for 'if' on next line
Singletons ([Class sharedInstance])
Dot Notation (self.foo vs [self foo])
Max nested square brackets ([[[[ ]]]])




                                           2012
Not "Bad Code"?
   At least for purposes of this talk...
Braces for 'if' on next line
Singletons ([Class sharedInstance])
Dot Notation (self.foo vs [self foo])
Max nested square brackets ([[[[ ]]]])
ARC-less-ness (at least not yet)



                                           2012
Not "Bad Code"?
   At least for purposes of this talk...
Braces for 'if' on next line
Singletons ([Class sharedInstance])
Dot Notation (self.foo vs [self foo])
Max nested square brackets ([[[[ ]]]])
ARC-less-ness (at least not yet)
Lack of Unit Tests (Unfortunately)

                                           2012
"Out Of Scope"
          Bad Code
   At least for purposes of this talk...
Wrong in any Language
 Methods way too long
  (Including AppDelegate)
 "Stringly Typed" code
  Using strings instead of types
      or Classes or enums
General Programming 101 Stuff
                                           2012
Notice:
All of the code you are about to see
is real.
All of it was either released to users,
sold for real money or posted online.
Variable names have been changed to
protect the guilty.
Some has been edited for brevity.

                                          2012
Bad Code
                while (self && [self retainCount] >= 0) {
                    [self release];
                }
                ///Carl's Note: retainCount is NSUInteger
"Tier 1" Bad:
                if (--retainCount == 0) [self dealloc];


                - (id)initWithTarget:(id)theTarget
                {
                    retainCount = 1;

                    // We do not retain




                                                          2012
Bad Code
                while (self && [self retainCount] >= 0) {
                    [self release];
                }
                ///Carl's Note: retainCount is NSUInteger
"Tier 1" Bad:
                if (--retainCount == 0) [self dealloc];


                - (id)initWithTarget:(id)theTarget
                {
                    retainCount = 1;

                    // We do not retain




                                                          2012
Bad Code
                while (self && [self retainCount] >= 0) {
                    [self release];
                }
                ///Carl's Note: retainCount is NSUInteger
"Tier 1" Bad:
                if (--retainCount == 0) [self dealloc];


                - (id)initWithTarget:(id)theTarget
                {
                    retainCount = 1;

                    // We do not retain




                                                          2012
Bad Code
                while (self && [self retainCount] >= 0) {
                    [self release];
                }
                ///Carl's Note: retainCount is NSUInteger
"Tier 1" Bad:
                if (--retainCount == 0) [self dealloc];


                - (id)initWithTarget:(id)theTarget
                {
                    retainCount = 1;

                    // We do not retain




                                                          2012
Bad Code
                while (self && [self retainCount] >= 0) {
                    [self release];
                }
                ///Carl's Note: retainCount is NSUInteger
"Tier 1" Bad:
                if (--retainCount == 0) [self dealloc];


                - (id)initWithTarget:(id)theTarget
                {
                    retainCount = 1;

                    // We do not retain




                                                          2012
"Tier 1" Bad Code




Completely Useless   2012
Tier1: D.U.I.



http://xkcd.com/323/   2012
Tier1: D.U.I.
       Developing Under the Influence




http://xkcd.com/323/                   2012
Tier1: Exhaustion




                    2012
Tier 1:
   Rage
 Against
     the
Machine
            2012
"Tier 2" Bad Code




"Lost In Translation"   2012
Method Naming
- (EDAMNote *) getNote: (NSString *)
authenticationToken : (EDAMGuid) guid : (BOOL)
withContent : (BOOL) withResourcesData :
(BOOL) withResourcesRecognition : (BOOL)
withResourcesAlternateData; // throws
EDAMUserException *, EDAMSystemException *,
EDAMNotFoundException *, TException




                                                 2012
Method Naming
- (EDAMNote *) getNote: (NSString *)
authenticationToken : (EDAMGuid) guid : (BOOL)
withContent : (BOOL) withResourcesData :
(BOOL) withResourcesRecognition : (BOOL)
withResourcesAlternateData; // throws
EDAMUserException *, EDAMSystemException *,
EDAMNotFoundException *, TException

@selector(getNote::::::)


                                                 2012
Class Naming
@interface SettingsView :
          UIViewController
             <UITableViewDelegate,
             UITableViewDataSource,
             UITextFieldDelegate>




                                      2012
Don't re-init existing objects


NSArray *barItems = _tabBarController.tabBar.items;
UIImage *image = [UIImage imageNamed:@"barImage0.png"];
[[barItems objectAtIndex:0]
      initWithTitle:@"Title0" image:image tag:0];




                                                     2012
Read Good Code
 We don't expect people to write in
 English before they spend a lot of time
 reading it
 Programming Languages should be no
 different
 Read Good Books and Blogs
 WWDC videos and Apple Sample Code

                                           2012
Read Good Code
 We don't expect people to write in
 English before they spend a lot of time
 reading it
 Programming Languages should be no
 different
 Read Good Books and Blogs
 WWDC videos and Apple Sample Code

                                           2012
Coding Style
if ([UIImagePickerController
       isSourceTypeAvailable:
         UIImagePickerControllerSourceTypeCamera])

    if ([[UIImagePickerController
       availableMediaTypesForSourceType:
           picker.sourceType] containsObject:
             kUTTypeMovie])

        self.cameraButton.hidden = NO;




                                                 2012
Coding Style
if ([UIImagePickerController
       isSourceTypeAvailable:
         UIImagePickerControllerSourceTypeCamera])

     self.title = @"Event Info";

    if ([[UIImagePickerController
       availableMediaTypesForSourceType:
           picker.sourceType] containsObject:
             kUTTypeMovie])

        self.cameraButton.hidden = NO;



                                                 2012
Coding Style
if ([UIImagePickerController
       isSourceTypeAvailable:
         UIImagePickerControllerSourceTypeCamera])

     self.title = @"Event Info";

if ([[UIImagePickerController
   availableMediaTypesForSourceType:
       picker.sourceType] containsObject:
         kUTTypeMovie])

    self.cameraButton.hidden = NO;



                                                 2012
Error vs. Exception
@try {
    // perform the request
    NSError *error;
    return [managedObjectContext
            executeFetchRequest:request
            error:&error];
}
@catch (NSException *exception) {
    NSLog(@"Problem Fetching: %@", exception);
}




                                                 2012
More NSError
- (BOOL) save {
    NSError *error = nil;
    BOOL res = [[self managedObjectContext]
                 save: &error];

    if (error != nil) {
        NSLog(@"Error saving data: %@", error);
    }
    return res;
}




                                                  2012
What if?
- (BOOL)writeFile:(NSString*)name
        error:(NSError**)error {
  if (![data writeToFile:name error:error]) {
    NSString *dir=[name lastPathComponent];
    if (![fileMgr fileExistsAtPath:dir]) {
      [fileMgr createDirectoryAtPath:dir
               error:error];
      if ([data writeToFile:name error:error]) {
                return YES;
      }
    }
    return NO;
  }
  return YES;
}

                                                   2012
Tries to Write File
- (BOOL)writeFile:(NSString*)name
        error:(NSError**)error {
  if (![data writeToFile:name error:error]) {
    NSString *dir=[name lastPathComponent];
    if (![fileMgr fileExistsAtPath:dir]) {
      [fileMgr createDirectoryAtPath:dir
               error:error];
      if ([data writeToFile:name error:error]) {
                return YES;
      }
    }
    return NO;
  }
  return YES;
}

                                                   2012
Mkdir if needed
- (BOOL)writeFile:(NSString*)name
        error:(NSError**)error {
  if (![data writeToFile:name error:error]) {
    NSString *dir=[name lastPathComponent];
    if (![fileMgr fileExistsAtPath:dir]) {
      [fileMgr createDirectoryAtPath:dir
               error:error];
      if ([data writeToFile:name error:error]) {
                return YES;
      }
    }
    return NO;
  }
  return YES;
}

                                                   2012
Then tries writing again
- (BOOL)writeFile:(NSString*)name
        error:(NSError**)error {
  if (![data writeToFile:name error:error]) {
    NSString *dir=[name lastPathComponent];
    if (![fileMgr fileExistsAtPath:dir]) {
      [fileMgr createDirectoryAtPath:dir
               error:error];
      if ([data writeToFile:name error:error]) {
                return YES;
      }
    }
    return NO;
  }
  return YES;
}

                                                   2012
What gets Returned?
- (BOOL)writeFile:(NSString*)name
        error:(NSError**)error {
  if (![data writeToFile:name error:error]) {
    NSString *dir=[name lastPathComponent];
    if (![fileMgr fileExistsAtPath:dir]) {
      [fileMgr createDirectoryAtPath:dir
               error:error];
      if ([data writeToFile:name error:error]) {
                return YES;
      }
    }
    return NO;
  }
  return YES;
}

                                                   2012
So, don't check error
 - (BOOL) save {
     NSError *error = nil;
     BOOL res = [[self managedObjectContext]
                  save: &error];

     if (error ! = nil) {
         NSLog(@"Error saving data: %@", error);
     }
     return res;
 }




                                                   2012
Check Return Value
- (BOOL) save {
    NSError *error = nil;
    BOOL res = [[self managedObjectContext]
                 save: &error];

    if (!res ) {
        NSLog(@"Error saving data: %@", error);
    }
    return res;
}




                                                  2012
Threading...
  [NSThread detachNewThreadSelector:
      @selector(fetchImageOnNewThread:)
        toTarget:self withObject:connDict];


-(void) fetchImageOnNewThread:(NSDictionary *) d {
    _image = [UIImage imageWithData:
      [NSData dataWithContentsOfURL:
           [d objectForKey:@"URL"]]];
}




                                                     2012
Asynchronous iOS
                           Programming Rules
                                         Threads are bad
                                         Use Queues instead
                                         Apple's Concurrency Programming
                                         Guide*:
                                              Page 10: "The Move Away from Threads"
                                              Page 74: "Migrating Away from Threads"
                                              Page 74: "Replacing Threads with
                                              Dispatch Queues"

* http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html   2012
...or Lack Thereof
-(IBAction)uploadAll {
  if ([uploadQueueArray count] > 0) {
    for (NSDictionary *item in uploadQueueArray) {
        [self.uploader connect:[item objectForKey:@"servername"]];
        [self.uploader authenticate:[item objectForKey:@"user"]
               withPassword:[item objectForKey:@"pass"]];
        [self.uploader uploadFile:[videoArray objectAtIndex:0]];
    }
  }
}




                                                                     2012
Operation vs Dispatch
 I tend to use NSOperations for:
  things I'm going to do several times
  things that have non-trivial complexity




                                            2012
Operation vs Dispatch
 I tend to use NSOperations for:
  things I'm going to do several times
  things that have non-trivial complexity
 I tend to use dispatch_async() for things:
  with less than 10 or so lines of code
  done only once in the App
  that won't need to change when spec changes



                                                2012
Waiting
  [NSThread sleepForTimeInterval:0.5];




! // wait until the operation is completed
  !
  [(NSConditionLock *) operation.doneLock
  lockWhenCondition:kXmlOperationComplete
    beforeDate:[NSDate dateWithTimeIntervalSinceNow:
        kGiveUpInterval]];



                                                       2012
Waiting
      [[NSRunLoop currentRunLoop]
            runUntilDate:[NSDate
           dateWithTimeIntervalSinceNow:2.0]];




- (void)main {
    //Do Stuff
    CFRunLoopRun();
}

-(void) finish {
    CFRunLoopStop(CFRunLoopGetCurrent());
}

                                                 2012
Password Storage
@interface Uploads : NSManagedObject
@property (nonatomic, retain) NSString * username;
@property (nonatomic, retain) NSString * password;
@end

@implementation Uploads
@dynamic username;
@dynamic password;
@end



  password.text =
  [[NSUserDefaults standardUserDefaults]
               valueForKey:kPasswordPreference];

                                                     2012
Password Storage



 Use the Keychain. These will help.   2012
Date Stuff
    monthsArray = [[NSArray alloc]
initWithObjects:NSLocalizedString(@"January",
@"January"), NSLocalizedString(@"February",
@"February"), NSLocalizedString(@"March", @"March"),
NSLocalizedString(@"April", @"April"),
NSLocalizedString(@"May", @"May"),
NSLocalizedString(@"June", @"June"),
NSLocalizedString(@"July", @"July"),
NSLocalizedString(@"August", @"August"),
NSLocalizedString(@"September",@"September"),
NSLocalizedString(@"October",@"October"),
NSLocalizedString(@"November", @"November"),
NSLocalizedString(@"December", @"December"), nil];


                                                       2012
Date Stuff

NSDateFormatter *dateFormatter =
                 [NSDateFormatter new];
monthsArray = [dateFormatter monthSymbols];




 Use NSDateFormatters. But not too many.      2012
Careful with Caching
fetcher = [fetcherMap objectForKey:@"type"];
if (!fetcher) {
        fetcher = [[Fetcher alloc] init];
        [fetcherMap setValue:fetcher
                 forKey:@"type"];
        [fetcher release];
}

[ fetcher fetchPicturesForDelegate:self
     callbackMethod:@selector(picsFound:error:)];




  Where's the (intermittent) crash here?            2012
Careful with Caching
fetcher = [ nil objectForKey:@"type"];
if (!fetcher) {
        fetcher = [[Fetcher alloc] init];
        [ nil setValue:fetcher
                 forKey:@"type"];
        [fetcher release];
}

[ fetcher fetchPicturesForDelegate:self
     callbackMethod:@selector(picsFound:error:)];




         Cache turned out to be nil                 2012
Careful with Caching
fetcher = [ nil objectForKey:@"type"];
if (!fetcher) {
        fetcher = [[Fetcher alloc] init];
        [ nil setValue:fetcher
                 forKey:@"type"];
        [fetcher release];
}

[NSZombie fetchPicturesForDelegate:self
    callbackMethod:@selector(picsFound:error:)];




    So fetcher could Zombify anytime               2012
Don't Cache That Way
    That's a paradigm from web
    programming, not mobile
    Good only when you have tons of
    memory
     and need to serve >1K req/sec
    Mobile needs to be Responsive,
     but that kind of memory/time tradeoff not
     helpful

                                                 2012
"Tier 3" Bad Code




     "TL;DR"        2012
Tags
UITableViewCell *cell = [tableView
      dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *dateLabel = (UILabel *)[cell viewWithTag:1];
UIView *imageBackground = (UIView *)[cell viewWithTag:2];
UIImageView *thumbnail = (UIImageView *)[cell viewWithTag:3];




                                                            2012
Tags
UITableViewCell *cell = [tableView
      dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *dateLabel = (UILabel *)[cell viewWithTag:1];
UIView *imageBackground = (UIView *)[cell viewWithTag:2];
UIImageView *thumbnail = (UIImageView *)[cell viewWithTag:3];




     https://twitter.com/drance/status/226011326273695745   2012
Tags should be a Last Resort

  Primarily used for:
   Multiple Instances of the same Class
   With the same Delegate method




                                          2012
Tags should be a Last Resort

  Primarily used for:
   Multiple Instances of the same Class
   With the same Delegate method
  Like UIAlertView or UIActionSheet




                                          2012
Tags should be a Last Resort

  Primarily used for:
   Multiple Instances of the same Class
   With the same Delegate method
  Like UIAlertView or UIActionSheet
  also -[MKMapViewDelegate
  mapView:viewForAnnotation:]




                                          2012
Don't "Remember"
          Core Data Results
- (void)viewDidLoad
{
    self.results =
       [self.fetchedResultsController fetchedObjects];
! //or
    self.results = [self.managedObjectContext
       executeFetchRequest:fetchRequest error:&error];
}

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   cell.textLabel.text =
   [[self.results objectAtIndex:indexPath.row] name];




                                                          2012
Ask the
 NSFetchedResultsController

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.textLabel.text =
           [self.fetchedResultsController
              objectAtIndexPath:indexPath];




                                                          2012
Don't mix read-only and
    read-write data
Imagine an App that downloads bulk
data like phonebooks or store
locations or SKUs




                                     2012
Don't mix read-only and
    read-write data
Imagine an App that downloads bulk
data like phonebooks or store
locations or SKUs
Now imagine that App also lets the
local user comment/favorite/rate/
review those entries



                                     2012
Don't mix read-only and
    read-write data
Imagine an App that downloads bulk
data like phonebooks or store
locations or SKUs
Now imagine that App also lets the
local user comment/favorite/rate/
review those entries
Now imagine that every time you
updated 400K records, you had to
preserve local modifications...
                                     2012
NSOperations have Overhead


NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation =
             [[NSInvocationOperation alloc]
               initWithTarget:self
               selector:@selector(doSomeThing)
               object:nil];
[queue addOperation:operation];
[operation release];
[queue release];




                                                    2012
Use GCD for one-offs

dispatch_async(dispatch_get_global_queue(DISPATCH_
QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [self
         performSelector:@selector(doSomeThing)];
    });




                                                     2012
"Tier 3" Bad Code




   "It's a Trap!!"   2012
"Tier 3" Bad Code




   "It's a Trap!!"   2012
This is my scariest slide




                            2012
This is my scariest slide




                            2012
Three20



http://twitter.com/joehewitt/status/118437722519121920   2012
Three20 cont.
the new Facebook for iOS marks our first
release in years without the Three20
framework. 




    https://www.facebook.com/notes/facebook-engineering/
under-the-hood-rebuilding-facebook-for-ios/10151036091753920   2012
asi-http-request

After giving it a lot of thought over the
last few weeks, I’ve decided that I’m not
going to continue working on
ASIHTTPRequest in future.


http://allseeing-i.com/%5Brequest_release%5D;   2012
Async != Background
- (void)viewDidLoad {
! ///Snip...
! [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSString *jsonString = [[NSString alloc]
               initWithData:jsonResponse
                 encoding:NSUTF8StringEncoding];

      for (NSDictionary *jsonDict in [jsonString JSONValue]) {
!   ! //Create Object
!   ! [NSEntityDescription insertNewObjectForEntityForName:@"Event"];
!   }
}



      AFNetworking can have this confusion                           2012
"Rocket Engine" for
 Responsive Code




                      2012
"Rocket Engine" for
         Responsive Code


NSAssert(![NSThread isMainThread], @"BOOM");




                                           2012
"Rocket Engine" for
         Responsive Code
NSAssert(![NSThread isMainThread], @"BOOM");


        WARNING: Rocket Engines




         can EXPLODE in testing.
                                           2012
Updating from Network
- (void)eventModelDidLoad:(NSArray *)events
{
! for (UIImageView *poster in
           self.scrollView.contentView.subViews) {
        [poster removeFromSuperview];
  }
  for (Event *event in events)
  {
    UIImageView *poster = [[UIImageView alloc]
                  initWithImage:event.image];
    [eventPoster setFrame:event.frameRect];
    [self.scrollView.contentView addSubview:poster];
  }
}
     Typical of a RESTKit Project                      2012
Refresh Incrementally

   Only Init the Subviews Where
   User Might Scroll Soon
    Think tableView and
    dequeueReusableCellWithIdentifier
   Don't Delete & Recreate
   Subviews that Already Exist


                                       2012
MVC
 Model




         2012
MVC
 Model

         Controller



 View
                      2012
NMVC
Network   Model (or at least "Disk")

                     Controller



          View
                                       2012
NMVC
Network   Model (or at least "Disk")

                     Controller



          View
                                       2012
Networking Advice
         Always* load the UI from local storage
           WITHOUT WAITING or BLOCKING
           Core Data or local file or something
           Put up a placeholder if no data




*Except with live web pages or HTTP streaming or the like   2012
Networking Advice
         Always* load the UI from local storage
           WITHOUT WAITING or BLOCKING
           Core Data or local file or something
           Put up a placeholder if no data
         Then start async download




*Except with live web pages or HTTP streaming or the like   2012
Networking Advice
         Always* load the UI from local storage
           WITHOUT WAITING or BLOCKING
           Core Data or local file or something
           Put up a placeholder if no data
         Then start async download
         Then put network data in local storage


*Except with live web pages or HTTP streaming or the like   2012
Networking Advice
         Always* load the UI from local storage
           WITHOUT WAITING or BLOCKING
           Core Data or local file or something
           Put up a placeholder if no data
         Then start async download
         Then put network data in local storage
         Then notify the UI to refresh itself

*Except with live web pages or HTTP streaming or the like   2012
In Summary
Program Sober and Rested, If Possible
Leave Other Languages' Patterns
    at the Door
Program Idiomatically
Don't Fight the Frameworks
Read the Fine Print
Look for Hidden Bugs
                                        2012
Thank You
 CarlB@PDAgent.com
      @CarlBrwn

                     2012

Weitere ähnliche Inhalte

Was ist angesagt?

Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"
Victor_Cr
 
Testing untestable code - oscon 2012
Testing untestable code - oscon 2012Testing untestable code - oscon 2012
Testing untestable code - oscon 2012
Stephan Hochdörfer
 

Was ist angesagt? (20)

The Xtext Grammar Language
The Xtext Grammar LanguageThe Xtext Grammar Language
The Xtext Grammar Language
 
Writing clean code
Writing clean codeWriting clean code
Writing clean code
 
Solid principles
Solid principlesSolid principles
Solid principles
 
Getting started with the JNI
Getting started with the JNIGetting started with the JNI
Getting started with the JNI
 
Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"
 
Clean Code Development
Clean Code DevelopmentClean Code Development
Clean Code Development
 
Objective-C Crash Course for Web Developers
Objective-C Crash Course for Web DevelopersObjective-C Crash Course for Web Developers
Objective-C Crash Course for Web Developers
 
Messages, Instances and Initialization
Messages, Instances and InitializationMessages, Instances and Initialization
Messages, Instances and Initialization
 
S313431 JPA 2.0 Overview
S313431 JPA 2.0 OverviewS313431 JPA 2.0 Overview
S313431 JPA 2.0 Overview
 
Enforce Consistentcy with Clean Architecture
Enforce Consistentcy with Clean ArchitectureEnforce Consistentcy with Clean Architecture
Enforce Consistentcy with Clean Architecture
 
Using Xcore with Xtext
Using Xcore with XtextUsing Xcore with Xtext
Using Xcore with Xtext
 
Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009
 
Dependency Injection Why is it awesome and Why should I care?
Dependency Injection Why is it awesome and Why should I care?Dependency Injection Why is it awesome and Why should I care?
Dependency Injection Why is it awesome and Why should I care?
 
Introduction to OO, Java and Eclipse/WebSphere
Introduction to OO, Java and Eclipse/WebSphereIntroduction to OO, Java and Eclipse/WebSphere
Introduction to OO, Java and Eclipse/WebSphere
 
iPhone Seminar Part 2
iPhone Seminar Part 2iPhone Seminar Part 2
iPhone Seminar Part 2
 
Iphone course 1
Iphone course 1Iphone course 1
Iphone course 1
 
From C++ to Objective-C
From C++ to Objective-CFrom C++ to Objective-C
From C++ to Objective-C
 
Reversing JavaScript
Reversing JavaScriptReversing JavaScript
Reversing JavaScript
 
Condor overview - glideinWMS Training Jan 2012
Condor overview - glideinWMS Training Jan 2012Condor overview - glideinWMS Training Jan 2012
Condor overview - glideinWMS Training Jan 2012
 
Testing untestable code - oscon 2012
Testing untestable code - oscon 2012Testing untestable code - oscon 2012
Testing untestable code - oscon 2012
 

Ähnlich wie 360iDev iOS AntiPatterns

Ähnlich wie 360iDev iOS AntiPatterns (20)

Practical JavaScript Programming - Session 4/8
Practical JavaScript Programming - Session 4/8Practical JavaScript Programming - Session 4/8
Practical JavaScript Programming - Session 4/8
 
Robots in Swift
Robots in SwiftRobots in Swift
Robots in Swift
 
From clever code to better code
From clever code to better codeFrom clever code to better code
From clever code to better code
 
Clean Code 2
Clean Code 2Clean Code 2
Clean Code 2
 
Thinking In Swift
Thinking In SwiftThinking In Swift
Thinking In Swift
 
Test02
Test02Test02
Test02
 
Dojo >= 1.7 Kickstart
Dojo >= 1.7  KickstartDojo >= 1.7  Kickstart
Dojo >= 1.7 Kickstart
 
Working with c++ legacy code
Working with c++ legacy codeWorking with c++ legacy code
Working with c++ legacy code
 
iOS training (basic)
iOS training (basic)iOS training (basic)
iOS training (basic)
 
Jailbreak Detector Detector
Jailbreak Detector DetectorJailbreak Detector Detector
Jailbreak Detector Detector
 
Android Dev Study Jam.pptx
Android Dev Study Jam.pptxAndroid Dev Study Jam.pptx
Android Dev Study Jam.pptx
 
Dependency injectionpreso
Dependency injectionpresoDependency injectionpreso
Dependency injectionpreso
 
IOS debugging
IOS debuggingIOS debugging
IOS debugging
 
ONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICES
ONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICESONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICES
ONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICES
 
Supercharge Flutter declarative UI with code generation
Supercharge Flutter declarative UI with code generationSupercharge Flutter declarative UI with code generation
Supercharge Flutter declarative UI with code generation
 
Android Dev Study Jam.pptx
Android Dev Study Jam.pptxAndroid Dev Study Jam.pptx
Android Dev Study Jam.pptx
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS Architecture
 
AngularJS Internal
AngularJS InternalAngularJS Internal
AngularJS Internal
 
Android | Busy Java Developers Guide to Android: Persistence | Ted Neward
Android | Busy Java Developers Guide to Android: Persistence | Ted NewardAndroid | Busy Java Developers Guide to Android: Persistence | Ted Neward
Android | Busy Java Developers Guide to Android: Persistence | Ted Neward
 
.NET Coding Standards For The Real World (2012)
.NET Coding Standards For The Real World (2012).NET Coding Standards For The Real World (2012)
.NET Coding Standards For The Real World (2012)
 

Mehr von Carl Brown

Cocoa coders 141113-watch
Cocoa coders 141113-watchCocoa coders 141113-watch
Cocoa coders 141113-watch
Carl Brown
 

Mehr von Carl Brown (20)

GDPR, User Data, Privacy, and Your Apps
GDPR, User Data, Privacy, and Your AppsGDPR, User Data, Privacy, and Your Apps
GDPR, User Data, Privacy, and Your Apps
 
New in iOS 11.3b4 and Xcode 9.3b4
New in iOS 11.3b4 and Xcode 9.3b4New in iOS 11.3b4 and Xcode 9.3b4
New in iOS 11.3b4 and Xcode 9.3b4
 
Managing Memory in Swift (Yes, that's a thing)
Managing Memory in Swift (Yes, that's a thing)Managing Memory in Swift (Yes, that's a thing)
Managing Memory in Swift (Yes, that's a thing)
 
Better Swift from the Foundation up #tryswiftnyc17 09-06
Better Swift from the Foundation up #tryswiftnyc17 09-06Better Swift from the Foundation up #tryswiftnyc17 09-06
Better Swift from the Foundation up #tryswiftnyc17 09-06
 
Generics, the Swift ABI and you
Generics, the Swift ABI and youGenerics, the Swift ABI and you
Generics, the Swift ABI and you
 
Swift GUI Development without Xcode
Swift GUI Development without XcodeSwift GUI Development without Xcode
Swift GUI Development without Xcode
 
what's new in iOS10 2016-06-23
what's new in iOS10 2016-06-23what's new in iOS10 2016-06-23
what's new in iOS10 2016-06-23
 
Open Source Swift: Up and Running
Open Source Swift: Up and RunningOpen Source Swift: Up and Running
Open Source Swift: Up and Running
 
Parse migration CocoaCoders April 28th, 2016
Parse migration CocoaCoders April 28th, 2016Parse migration CocoaCoders April 28th, 2016
Parse migration CocoaCoders April 28th, 2016
 
Swift 2.2 Design Patterns CocoaConf Austin 2016
Swift 2.2 Design Patterns CocoaConf Austin 2016Swift 2.2 Design Patterns CocoaConf Austin 2016
Swift 2.2 Design Patterns CocoaConf Austin 2016
 
Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...
Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...
Advanced, Composable Collection Views, From CocoaCoders meetup Austin Feb 12,...
 
Gcd cc-150205
Gcd cc-150205Gcd cc-150205
Gcd cc-150205
 
Cocoa coders 141113-watch
Cocoa coders 141113-watchCocoa coders 141113-watch
Cocoa coders 141113-watch
 
iOS8 and the new App Store
iOS8 and the new App Store   iOS8 and the new App Store
iOS8 and the new App Store
 
Dark Art of Software Estimation 360iDev2014
Dark Art of Software Estimation 360iDev2014Dark Art of Software Estimation 360iDev2014
Dark Art of Software Estimation 360iDev2014
 
Intro to cloud kit Cocoader.org 24 July 2014
Intro to cloud kit   Cocoader.org 24 July 2014Intro to cloud kit   Cocoader.org 24 July 2014
Intro to cloud kit Cocoader.org 24 July 2014
 
Welcome to Swift (CocoaCoder 6/12/14)
Welcome to Swift (CocoaCoder 6/12/14)Welcome to Swift (CocoaCoder 6/12/14)
Welcome to Swift (CocoaCoder 6/12/14)
 
Writing Apps that Can See: Getting Data from CoreImage to Computer Vision - ...
Writing Apps that Can See: Getting Data from CoreImage to Computer  Vision - ...Writing Apps that Can See: Getting Data from CoreImage to Computer  Vision - ...
Writing Apps that Can See: Getting Data from CoreImage to Computer Vision - ...
 
Introduction to Git Commands and Concepts
Introduction to Git Commands and ConceptsIntroduction to Git Commands and Concepts
Introduction to Git Commands and Concepts
 
REST/JSON/CoreData Example Code - A Tour
REST/JSON/CoreData Example Code - A TourREST/JSON/CoreData Example Code - A Tour
REST/JSON/CoreData Example Code - A Tour
 

Kürzlich hochgeladen

Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 

Kürzlich hochgeladen (20)

2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 

360iDev iOS AntiPatterns

  • 1. "Stop it! That Hurts!" Common iOS Anti-Patterns @CarlBrwn Carl Brown Missing final vowel (blame Flickr) 2012
  • 2. ObBio Carl Brown (@CarlBrwn) iOS Contractor Teach Intro to iOS classes Co-Organizer CocoaCoder.org Started Mobile Programming 2004 (Palm) Half of team (Nov. '08) that wrote LIVESTRONG.com Calorie Tracker 2012
  • 3. ObBio Carl Brown (@CarlBrwn) iOS Contractor Teach Intro to iOS classes Co-Organizer CocoaCoder.org Started Mobile Programming 2004 (Palm) Half of team (Nov. '08) that wrote LIVESTRONG.com Calorie Tracker 2012
  • 4. Why So Much Bad Code? I live in Austin Texas 2012
  • 5. Why So Much Bad Code? "Looks like any idiot can make an App" 2012
  • 6. Why So Much Bad Code? I try not to travel 2012
  • 7. Why So Much Bad Code? Working with clients in Austin Where SxSW implies that any idiot can found App startup oDesk/Elance/etc are full of idiots 2008 NDA gave idiots a running start 2012
  • 8. Not "Bad Code"? At least for purposes of this talk... Braces for 'if' on next line 2012
  • 9. Not "Bad Code"? At least for purposes of this talk... Braces for 'if' on next line Singletons ([Class sharedInstance]) 2012
  • 10. Not "Bad Code"? At least for purposes of this talk... Braces for 'if' on next line Singletons ([Class sharedInstance]) Dot Notation (self.foo vs [self foo]) 2012
  • 11. Not "Bad Code"? At least for purposes of this talk... Braces for 'if' on next line Singletons ([Class sharedInstance]) Dot Notation (self.foo vs [self foo]) Max nested square brackets ([[[[ ]]]]) 2012
  • 12. Not "Bad Code"? At least for purposes of this talk... Braces for 'if' on next line Singletons ([Class sharedInstance]) Dot Notation (self.foo vs [self foo]) Max nested square brackets ([[[[ ]]]]) ARC-less-ness (at least not yet) 2012
  • 13. Not "Bad Code"? At least for purposes of this talk... Braces for 'if' on next line Singletons ([Class sharedInstance]) Dot Notation (self.foo vs [self foo]) Max nested square brackets ([[[[ ]]]]) ARC-less-ness (at least not yet) Lack of Unit Tests (Unfortunately) 2012
  • 14. "Out Of Scope" Bad Code At least for purposes of this talk... Wrong in any Language Methods way too long (Including AppDelegate) "Stringly Typed" code Using strings instead of types or Classes or enums General Programming 101 Stuff 2012
  • 15. Notice: All of the code you are about to see is real. All of it was either released to users, sold for real money or posted online. Variable names have been changed to protect the guilty. Some has been edited for brevity. 2012
  • 16. Bad Code while (self && [self retainCount] >= 0) { [self release]; } ///Carl's Note: retainCount is NSUInteger "Tier 1" Bad: if (--retainCount == 0) [self dealloc]; - (id)initWithTarget:(id)theTarget { retainCount = 1; // We do not retain 2012
  • 17. Bad Code while (self && [self retainCount] >= 0) { [self release]; } ///Carl's Note: retainCount is NSUInteger "Tier 1" Bad: if (--retainCount == 0) [self dealloc]; - (id)initWithTarget:(id)theTarget { retainCount = 1; // We do not retain 2012
  • 18. Bad Code while (self && [self retainCount] >= 0) { [self release]; } ///Carl's Note: retainCount is NSUInteger "Tier 1" Bad: if (--retainCount == 0) [self dealloc]; - (id)initWithTarget:(id)theTarget { retainCount = 1; // We do not retain 2012
  • 19. Bad Code while (self && [self retainCount] >= 0) { [self release]; } ///Carl's Note: retainCount is NSUInteger "Tier 1" Bad: if (--retainCount == 0) [self dealloc]; - (id)initWithTarget:(id)theTarget { retainCount = 1; // We do not retain 2012
  • 20. Bad Code while (self && [self retainCount] >= 0) { [self release]; } ///Carl's Note: retainCount is NSUInteger "Tier 1" Bad: if (--retainCount == 0) [self dealloc]; - (id)initWithTarget:(id)theTarget { retainCount = 1; // We do not retain 2012
  • 21. "Tier 1" Bad Code Completely Useless 2012
  • 23. Tier1: D.U.I. Developing Under the Influence http://xkcd.com/323/ 2012
  • 25. Tier 1: Rage Against the Machine 2012
  • 26. "Tier 2" Bad Code "Lost In Translation" 2012
  • 27. Method Naming - (EDAMNote *) getNote: (NSString *) authenticationToken : (EDAMGuid) guid : (BOOL) withContent : (BOOL) withResourcesData : (BOOL) withResourcesRecognition : (BOOL) withResourcesAlternateData; // throws EDAMUserException *, EDAMSystemException *, EDAMNotFoundException *, TException 2012
  • 28. Method Naming - (EDAMNote *) getNote: (NSString *) authenticationToken : (EDAMGuid) guid : (BOOL) withContent : (BOOL) withResourcesData : (BOOL) withResourcesRecognition : (BOOL) withResourcesAlternateData; // throws EDAMUserException *, EDAMSystemException *, EDAMNotFoundException *, TException @selector(getNote::::::) 2012
  • 29. Class Naming @interface SettingsView : UIViewController <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate> 2012
  • 30. Don't re-init existing objects NSArray *barItems = _tabBarController.tabBar.items; UIImage *image = [UIImage imageNamed:@"barImage0.png"]; [[barItems objectAtIndex:0] initWithTitle:@"Title0" image:image tag:0]; 2012
  • 31. Read Good Code We don't expect people to write in English before they spend a lot of time reading it Programming Languages should be no different Read Good Books and Blogs WWDC videos and Apple Sample Code 2012
  • 32. Read Good Code We don't expect people to write in English before they spend a lot of time reading it Programming Languages should be no different Read Good Books and Blogs WWDC videos and Apple Sample Code 2012
  • 33. Coding Style if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) if ([[UIImagePickerController availableMediaTypesForSourceType: picker.sourceType] containsObject: kUTTypeMovie]) self.cameraButton.hidden = NO; 2012
  • 34. Coding Style if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) self.title = @"Event Info"; if ([[UIImagePickerController availableMediaTypesForSourceType: picker.sourceType] containsObject: kUTTypeMovie]) self.cameraButton.hidden = NO; 2012
  • 35. Coding Style if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) self.title = @"Event Info"; if ([[UIImagePickerController availableMediaTypesForSourceType: picker.sourceType] containsObject: kUTTypeMovie]) self.cameraButton.hidden = NO; 2012
  • 36. Error vs. Exception @try { // perform the request NSError *error; return [managedObjectContext executeFetchRequest:request error:&error]; } @catch (NSException *exception) { NSLog(@"Problem Fetching: %@", exception); } 2012
  • 37. More NSError - (BOOL) save { NSError *error = nil; BOOL res = [[self managedObjectContext] save: &error]; if (error != nil) { NSLog(@"Error saving data: %@", error); } return res; } 2012
  • 38. What if? - (BOOL)writeFile:(NSString*)name error:(NSError**)error { if (![data writeToFile:name error:error]) { NSString *dir=[name lastPathComponent]; if (![fileMgr fileExistsAtPath:dir]) { [fileMgr createDirectoryAtPath:dir error:error]; if ([data writeToFile:name error:error]) { return YES; } } return NO; } return YES; } 2012
  • 39. Tries to Write File - (BOOL)writeFile:(NSString*)name error:(NSError**)error { if (![data writeToFile:name error:error]) { NSString *dir=[name lastPathComponent]; if (![fileMgr fileExistsAtPath:dir]) { [fileMgr createDirectoryAtPath:dir error:error]; if ([data writeToFile:name error:error]) { return YES; } } return NO; } return YES; } 2012
  • 40. Mkdir if needed - (BOOL)writeFile:(NSString*)name error:(NSError**)error { if (![data writeToFile:name error:error]) { NSString *dir=[name lastPathComponent]; if (![fileMgr fileExistsAtPath:dir]) { [fileMgr createDirectoryAtPath:dir error:error]; if ([data writeToFile:name error:error]) { return YES; } } return NO; } return YES; } 2012
  • 41. Then tries writing again - (BOOL)writeFile:(NSString*)name error:(NSError**)error { if (![data writeToFile:name error:error]) { NSString *dir=[name lastPathComponent]; if (![fileMgr fileExistsAtPath:dir]) { [fileMgr createDirectoryAtPath:dir error:error]; if ([data writeToFile:name error:error]) { return YES; } } return NO; } return YES; } 2012
  • 42. What gets Returned? - (BOOL)writeFile:(NSString*)name error:(NSError**)error { if (![data writeToFile:name error:error]) { NSString *dir=[name lastPathComponent]; if (![fileMgr fileExistsAtPath:dir]) { [fileMgr createDirectoryAtPath:dir error:error]; if ([data writeToFile:name error:error]) { return YES; } } return NO; } return YES; } 2012
  • 43. So, don't check error - (BOOL) save { NSError *error = nil; BOOL res = [[self managedObjectContext] save: &error]; if (error ! = nil) { NSLog(@"Error saving data: %@", error); } return res; } 2012
  • 44. Check Return Value - (BOOL) save { NSError *error = nil; BOOL res = [[self managedObjectContext] save: &error]; if (!res ) { NSLog(@"Error saving data: %@", error); } return res; } 2012
  • 45. Threading... [NSThread detachNewThreadSelector: @selector(fetchImageOnNewThread:) toTarget:self withObject:connDict]; -(void) fetchImageOnNewThread:(NSDictionary *) d { _image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [d objectForKey:@"URL"]]]; } 2012
  • 46. Asynchronous iOS Programming Rules Threads are bad Use Queues instead Apple's Concurrency Programming Guide*: Page 10: "The Move Away from Threads" Page 74: "Migrating Away from Threads" Page 74: "Replacing Threads with Dispatch Queues" * http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html 2012
  • 47. ...or Lack Thereof -(IBAction)uploadAll { if ([uploadQueueArray count] > 0) { for (NSDictionary *item in uploadQueueArray) { [self.uploader connect:[item objectForKey:@"servername"]]; [self.uploader authenticate:[item objectForKey:@"user"] withPassword:[item objectForKey:@"pass"]]; [self.uploader uploadFile:[videoArray objectAtIndex:0]]; } } } 2012
  • 48. Operation vs Dispatch I tend to use NSOperations for: things I'm going to do several times things that have non-trivial complexity 2012
  • 49. Operation vs Dispatch I tend to use NSOperations for: things I'm going to do several times things that have non-trivial complexity I tend to use dispatch_async() for things: with less than 10 or so lines of code done only once in the App that won't need to change when spec changes 2012
  • 50. Waiting [NSThread sleepForTimeInterval:0.5]; ! // wait until the operation is completed ! [(NSConditionLock *) operation.doneLock lockWhenCondition:kXmlOperationComplete beforeDate:[NSDate dateWithTimeIntervalSinceNow: kGiveUpInterval]]; 2012
  • 51. Waiting [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; - (void)main { //Do Stuff CFRunLoopRun(); } -(void) finish { CFRunLoopStop(CFRunLoopGetCurrent()); } 2012
  • 52. Password Storage @interface Uploads : NSManagedObject @property (nonatomic, retain) NSString * username; @property (nonatomic, retain) NSString * password; @end @implementation Uploads @dynamic username; @dynamic password; @end password.text = [[NSUserDefaults standardUserDefaults] valueForKey:kPasswordPreference]; 2012
  • 53. Password Storage Use the Keychain. These will help. 2012
  • 54. Date Stuff monthsArray = [[NSArray alloc] initWithObjects:NSLocalizedString(@"January", @"January"), NSLocalizedString(@"February", @"February"), NSLocalizedString(@"March", @"March"), NSLocalizedString(@"April", @"April"), NSLocalizedString(@"May", @"May"), NSLocalizedString(@"June", @"June"), NSLocalizedString(@"July", @"July"), NSLocalizedString(@"August", @"August"), NSLocalizedString(@"September",@"September"), NSLocalizedString(@"October",@"October"), NSLocalizedString(@"November", @"November"), NSLocalizedString(@"December", @"December"), nil]; 2012
  • 55. Date Stuff NSDateFormatter *dateFormatter = [NSDateFormatter new]; monthsArray = [dateFormatter monthSymbols]; Use NSDateFormatters. But not too many. 2012
  • 56. Careful with Caching fetcher = [fetcherMap objectForKey:@"type"]; if (!fetcher) { fetcher = [[Fetcher alloc] init]; [fetcherMap setValue:fetcher forKey:@"type"]; [fetcher release]; } [ fetcher fetchPicturesForDelegate:self callbackMethod:@selector(picsFound:error:)]; Where's the (intermittent) crash here? 2012
  • 57. Careful with Caching fetcher = [ nil objectForKey:@"type"]; if (!fetcher) { fetcher = [[Fetcher alloc] init]; [ nil setValue:fetcher forKey:@"type"]; [fetcher release]; } [ fetcher fetchPicturesForDelegate:self callbackMethod:@selector(picsFound:error:)]; Cache turned out to be nil 2012
  • 58. Careful with Caching fetcher = [ nil objectForKey:@"type"]; if (!fetcher) { fetcher = [[Fetcher alloc] init]; [ nil setValue:fetcher forKey:@"type"]; [fetcher release]; } [NSZombie fetchPicturesForDelegate:self callbackMethod:@selector(picsFound:error:)]; So fetcher could Zombify anytime 2012
  • 59. Don't Cache That Way That's a paradigm from web programming, not mobile Good only when you have tons of memory and need to serve >1K req/sec Mobile needs to be Responsive, but that kind of memory/time tradeoff not helpful 2012
  • 60. "Tier 3" Bad Code "TL;DR" 2012
  • 61. Tags UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; UILabel *dateLabel = (UILabel *)[cell viewWithTag:1]; UIView *imageBackground = (UIView *)[cell viewWithTag:2]; UIImageView *thumbnail = (UIImageView *)[cell viewWithTag:3]; 2012
  • 62. Tags UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; UILabel *dateLabel = (UILabel *)[cell viewWithTag:1]; UIView *imageBackground = (UIView *)[cell viewWithTag:2]; UIImageView *thumbnail = (UIImageView *)[cell viewWithTag:3]; https://twitter.com/drance/status/226011326273695745 2012
  • 63. Tags should be a Last Resort Primarily used for: Multiple Instances of the same Class With the same Delegate method 2012
  • 64. Tags should be a Last Resort Primarily used for: Multiple Instances of the same Class With the same Delegate method Like UIAlertView or UIActionSheet 2012
  • 65. Tags should be a Last Resort Primarily used for: Multiple Instances of the same Class With the same Delegate method Like UIAlertView or UIActionSheet also -[MKMapViewDelegate mapView:viewForAnnotation:] 2012
  • 66. Don't "Remember" Core Data Results - (void)viewDidLoad { self.results = [self.fetchedResultsController fetchedObjects]; ! //or self.results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { cell.textLabel.text = [[self.results objectAtIndex:indexPath.row] name]; 2012
  • 67. Ask the NSFetchedResultsController - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { cell.textLabel.text = [self.fetchedResultsController objectAtIndexPath:indexPath]; 2012
  • 68. Don't mix read-only and read-write data Imagine an App that downloads bulk data like phonebooks or store locations or SKUs 2012
  • 69. Don't mix read-only and read-write data Imagine an App that downloads bulk data like phonebooks or store locations or SKUs Now imagine that App also lets the local user comment/favorite/rate/ review those entries 2012
  • 70. Don't mix read-only and read-write data Imagine an App that downloads bulk data like phonebooks or store locations or SKUs Now imagine that App also lets the local user comment/favorite/rate/ review those entries Now imagine that every time you updated 400K records, you had to preserve local modifications... 2012
  • 71. NSOperations have Overhead NSOperationQueue *queue = [NSOperationQueue new]; NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil]; [queue addOperation:operation]; [operation release]; [queue release]; 2012
  • 72. Use GCD for one-offs dispatch_async(dispatch_get_global_queue(DISPATCH_ QUEUE_PRIORITY_BACKGROUND, 0), ^{ [self performSelector:@selector(doSomeThing)]; }); 2012
  • 73. "Tier 3" Bad Code "It's a Trap!!" 2012
  • 74. "Tier 3" Bad Code "It's a Trap!!" 2012
  • 75. This is my scariest slide 2012
  • 76. This is my scariest slide 2012
  • 78. Three20 cont. the new Facebook for iOS marks our first release in years without the Three20 framework.  https://www.facebook.com/notes/facebook-engineering/ under-the-hood-rebuilding-facebook-for-ios/10151036091753920 2012
  • 79. asi-http-request After giving it a lot of thought over the last few weeks, I’ve decided that I’m not going to continue working on ASIHTTPRequest in future. http://allseeing-i.com/%5Brequest_release%5D; 2012
  • 80. Async != Background - (void)viewDidLoad { ! ///Snip... ! [[NSURLConnection alloc] initWithRequest:request delegate:self]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSString *jsonString = [[NSString alloc] initWithData:jsonResponse encoding:NSUTF8StringEncoding]; for (NSDictionary *jsonDict in [jsonString JSONValue]) { ! ! //Create Object ! ! [NSEntityDescription insertNewObjectForEntityForName:@"Event"]; ! } } AFNetworking can have this confusion 2012
  • 81. "Rocket Engine" for Responsive Code 2012
  • 82. "Rocket Engine" for Responsive Code NSAssert(![NSThread isMainThread], @"BOOM"); 2012
  • 83. "Rocket Engine" for Responsive Code NSAssert(![NSThread isMainThread], @"BOOM"); WARNING: Rocket Engines can EXPLODE in testing. 2012
  • 84. Updating from Network - (void)eventModelDidLoad:(NSArray *)events { ! for (UIImageView *poster in self.scrollView.contentView.subViews) {         [poster removeFromSuperview];   }   for (Event *event in events)   {     UIImageView *poster = [[UIImageView alloc] initWithImage:event.image]; [eventPoster setFrame:event.frameRect];     [self.scrollView.contentView addSubview:poster];   } } Typical of a RESTKit Project 2012
  • 85. Refresh Incrementally Only Init the Subviews Where User Might Scroll Soon Think tableView and dequeueReusableCellWithIdentifier Don't Delete & Recreate Subviews that Already Exist 2012
  • 86. MVC Model 2012
  • 87. MVC Model Controller View 2012
  • 88. NMVC Network Model (or at least "Disk") Controller View 2012
  • 89. NMVC Network Model (or at least "Disk") Controller View 2012
  • 90. Networking Advice Always* load the UI from local storage WITHOUT WAITING or BLOCKING Core Data or local file or something Put up a placeholder if no data *Except with live web pages or HTTP streaming or the like 2012
  • 91. Networking Advice Always* load the UI from local storage WITHOUT WAITING or BLOCKING Core Data or local file or something Put up a placeholder if no data Then start async download *Except with live web pages or HTTP streaming or the like 2012
  • 92. Networking Advice Always* load the UI from local storage WITHOUT WAITING or BLOCKING Core Data or local file or something Put up a placeholder if no data Then start async download Then put network data in local storage *Except with live web pages or HTTP streaming or the like 2012
  • 93. Networking Advice Always* load the UI from local storage WITHOUT WAITING or BLOCKING Core Data or local file or something Put up a placeholder if no data Then start async download Then put network data in local storage Then notify the UI to refresh itself *Except with live web pages or HTTP streaming or the like 2012
  • 94. In Summary Program Sober and Rested, If Possible Leave Other Languages' Patterns at the Door Program Idiomatically Don't Fight the Frameworks Read the Fine Print Look for Hidden Bugs 2012
  • 95. Thank You CarlB@PDAgent.com @CarlBrwn 2012

Hinweis der Redaktion

  1. Start ScreenFlow and Camera!!!\n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. Often, this is just code written by stupid people\n
  23. Developing Under the Influence...\n
  24. But there&apos;s not a lot we can learn from looking at Tier1 Badness, so on to...\n
  25. \n
  26. Useful Somewhere, but not Here\n
  27. This is from one of my favorite public APIs to pick on: Evernote\n
  28. Please don&apos;t name your ViewControllers &quot;SomethingView&quot;\n
  29. \n
  30. \n
  31. This was a fun bug I had to chase down.\nI&apos;m pretty sure the original file looked like this\n
  32. And then I think this happened\n
  33. Which resulted in this file, which would sometimes present the camera button on devices without the camera, which would then crash if clicked.\n
  34. \n
  35. \n
  36. So Imagine there&apos;s a framework for saving a file\n
  37. \n
  38. \n
  39. \n
  40. So Imagine there&apos;s a framework for saving a file\n
  41. \n
  42. \n
  43. iOS app for company with multi-billion dollar market cap.\n
  44. \n
  45. But, it could be worse. This was a Flash programmer on a video uploading app.\n\nApparently, Flash doesn&apos;t do threads...\n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. In this case, fetcherMap was nil, so it would occasionally crash\n
  55. In this case, fetcherMap was nil, so it would occasionally crash\n
  56. In this case, fetcherMap was nil, so it would occasionally crash\n
  57. \n
  58. Apparently Useful, but not practically so\n
  59. So much for type safety\n
  60. MKMapViewDelegate Needed when user taps button on Callout on MapView \n
  61. MKMapViewDelegate Needed when user taps button on Callout on MapView \n
  62. MKMapViewDelegate Needed when user taps button on Callout on MapView \n
  63. MKMapViewDelegate Needed when user taps button on Callout on MapView \n
  64. Because if something in the background changes the data, the object in your array is no longer valid. Usually something like: &quot;CoreData could not fulfill a fault&quot;\n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. No matter what you think about this spider...\n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. So here, every time an update comes from the network, every poster is deleted from the scrollview, and then a new poster is made and added for everything that the network had. Even if it was already there. Even if it&apos;s pages away from where the user might scroll\n
  82. \n
  83. Network talks to the model, not the controller\n
  84. Network talks to the model, not the controller\n
  85. Network talks to the model, not the controller\n
  86. Network talks to the model, not the controller\n
  87. Network talks to the model, not the controller\n
  88. Network talks to the model, not the controller\n
  89. Network talks to the model, not the controller\n
  90. Network talks to the model, not the controller\n
  91. \n
  92. \n
  93. \n
  94. \n
  95. Stay away from things that make you Stupid\nRemember what language you&apos;re writing in\nPay attention, think things through, and educate yourself\n
  96. \n