What I’ve learned when developing BlockAlertViews

1,100 views

Published on

Published in: Technology, News & Politics
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,100
On SlideShare
0
From Embeds
0
Number of Embeds
38
Actions
Shares
0
Downloads
13
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

What I’ve learned when developing BlockAlertViews

  1. 1. What I’ve learned when developing BlockAlertViews Gustavo Ambrozio 360iDev 2012Tuesday, September 11, 12
  2. 2. About me • Developing professionally since age of 15Tuesday, September 11, 12
  3. 3. About me • Developing professionally since age of 15 • For iOS since the official SDK came outTuesday, September 11, 12
  4. 4. About me • Developing professionally since age of 15 • For iOS since the official SDK came out • Did some apps and contracts as an independent developer in BrazilTuesday, September 11, 12
  5. 5. About me • Developing professionally since age of 15 • For iOS since the official SDK came out • Did some apps and contracts as an independent developer in Brazil • Writter for raywenderlich.comTuesday, September 11, 12
  6. 6. About me • Developing professionally since age of 15 • For iOS since the official SDK came out • Did some apps and contracts as an independent developer in Brazil • Writter for raywenderlich.com • Hired by PocketGems to code games in SFTuesday, September 11, 12
  7. 7. About me • Developing professionally since age of 15 • For iOS since the official SDK came out • Did some apps and contracts as an independent developer in Brazil • Writter for raywenderlich.com • Hired by PocketGems to code games in SF • Hate engligh prepositionsTuesday, September 11, 12
  8. 8. What’s BlockAlertViewTuesday, September 11, 12
  9. 9. What’s BlockAlertViewTuesday, September 11, 12
  10. 10. What’s BlockAlertViewTuesday, September 11, 12
  11. 11. My love and hate story with UIAlertView • (apparently) Easy to use • Delegates • Switches • Tags • ConstantsTuesday, September 11, 12
  12. 12. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { UIAlertView *view = [[UIAlertView alloc] initWithTitle:@”Send to” message:@”Send message using:” delegate:self cancelButtonTitle:@”Cancel” otherButtonTitles:@”Twitter”, @”Facebook”, @”Email”, nil]; view.tag = kSendAlertView; [view show]; [view release]; }Tuesday, September 11, 12
  13. 13. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { UIAlertView *view = [[UIAlertView alloc] initWithTitle:@”Send to” message:@”Send message using:” delegate:self cancelButtonTitle:@”Cancel” otherButtonTitles:@”Twitter”, @”Facebook”, @”Email”, nil]; view.tag = kSendAlertView; [view show]; [view release]; }Tuesday, September 11, 12
  14. 14. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { UIAlertView *view = [[UIAlertView alloc] initWithTitle:@”Send to” message:@”Send message using:” delegate:self cancelButtonTitle:@”Cancel” otherButtonTitles:@”Twitter”, @”Facebook”, @”Email”, nil]; view.tag = kSendAlertView; [view show]; [view release]; }Tuesday, September 11, 12
  15. 15. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { UIAlertView *view = [[UIAlertView alloc] initWithTitle:@”Send to” message:@”Send message using:” delegate:self cancelButtonTitle:@”Cancel” otherButtonTitles:@”Twitter”, @”Facebook”, @”Email”, nil]; view.tag = kSendAlertView; [view show]; [view release]; }Tuesday, September 11, 12
  16. 16. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { switch (alertView.tag) { case kRemoveConfirmAlertView: if (buttonIndex == 1) [self removeItem]; break; case kSendAlertView: { switch (buttonIndex) { case 0: // Cancel break; case 1: // Twitter [self sendWith:kTwitter]; break; case 2: // Facebook [self sendWith:kFacebook]; break; case 3: // email MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; break; } } break; } }Tuesday, September 11, 12
  17. 17. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { switch (alertView.tag) { case kRemoveConfirmAlertView: if (buttonIndex == 1) [self removeItem]; break; case kSendAlertView: { switch (buttonIndex) { case 0: // Cancel break; case 1: // Twitter [self sendWith:kTwitter]; break; case 2: // Facebook [self sendWith:kFacebook]; break; case 3: // email MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; break; } } break; } }Tuesday, September 11, 12
  18. 18. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { switch (alertView.tag) { case kRemoveConfirmAlertView: if (buttonIndex == 1) [self removeItem]; break; case kSendAlertView: { switch (buttonIndex) { case 0: // Cancel break; case 1: // Twitter [self sendWith:kTwitter]; break; case 2: // Facebook [self sendWith:kFacebook]; break; case 3: // email MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; break; } } break; } }Tuesday, September 11, 12
  19. 19. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { switch (alertView.tag) { case kRemoveConfirmAlertView: if (buttonIndex == 1) [self removeItem]; break; case kSendAlertView: { switch (buttonIndex) { case 0: // Cancel break; case 1: // Twitter [self sendWith:kTwitter]; break; case 2: // Facebook [self sendWith:kFacebook]; break; case 3: // email MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; break; } } break; } }Tuesday, September 11, 12
  20. 20. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { switch (alertView.tag) { case kRemoveConfirmAlertView: if (buttonIndex == 1) [self removeItem]; break; case kSendAlertView: { switch (buttonIndex) { case 0: // Cancel break; case 1: // Twitter [self sendWith:kTwitter]; break; case 2: // Facebook [self sendWith:kFacebook]; break; case 3: // email MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; break; } } break; } }Tuesday, September 11, 12
  21. 21. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { switch (alertView.tag) { case kRemoveConfirmAlertView: if (buttonIndex == 1) [self removeItem]; break; case kSendAlertView: { switch (buttonIndex) { case 0: // Cancel break; case 1: // Twitter [self sendWith:kTwitter]; break; case 2: // Facebook [self sendWith:kFacebook]; break; case 3: // email MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; break; } } break; } }Tuesday, September 11, 12
  22. 22. How blocks ended my hatred • PSFoundation (https://github.com/steipete/ PSFoundation/tree/master/Utils) • Peter SteinbergerTuesday, September 11, 12
  23. 23. How blocks ended my hatred • PSFoundation (https://github.com/steipete/ PSFoundation/tree/master/Utils) • Peter Steinberger • PSAlertView e PSActionSheetTuesday, September 11, 12
  24. 24. How blocks ended my hatred • PSFoundation (https://github.com/steipete/ PSFoundation/tree/master/Utils) • Peter Steinberger • PSAlertView e PSActionSheet • Renamed to BlockAlertView and BlockActionSheetTuesday, September 11, 12
  25. 25. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlocAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view addButtonWithTitle:@”Email”, block:^{ MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; }]; view .tag = kSendAlertView; [view show]; }Tuesday, September 11, 12
  26. 26. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlocAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view addButtonWithTitle:@”Email”, block:^{ MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; }]; view .tag = kSendAlertView; [view show]; }Tuesday, September 11, 12
  27. 27. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlocAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view addButtonWithTitle:@”Email”, block:^{ MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; }]; view .tag = kSendAlertView; [view show]; }Tuesday, September 11, 12
  28. 28. @interface MyViewController : UIViewController <UIAlertViewDelegate> #define kRemoveConfirmAlertView 1 #define kSendAlertView 2 #define kLogoutConfirmAlertView 3 - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlocAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view addButtonWithTitle:@”Email”, block:^{ MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; }]; view .tag = kSendAlertView; [view show]; }Tuesday, September 11, 12
  29. 29. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view addButtonWithTitle:@”Email”, block:^{ MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; }]; [view show]; }Tuesday, September 11, 12
  30. 30. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view addButtonWithTitle:@”Email”, block:^{ MFMailComposeViewController *view = [[MFMailComposeViewController alloc] init]; view.mailComposeDelegate = self; [self.navigationController presentModalViewController:view animated:YES]; }]; [view show]; }Tuesday, September 11, 12
  31. 31. Lesson number 1 1. Use blocks instead of delegates (almost) all the timeTuesday, September 11, 12
  32. 32. Lesson number 1 1. Use blocks instead of delegates (almost) all the time • Exception: When the block can retain something that can be deallocated before que block call gets made (NSURLConnections for example)Tuesday, September 11, 12
  33. 33. How to do it 1. Subclass the original class and have it conform with it’s delegate. 2. Create typedefs for all blocks 3. Create @property for all blocks with (copy) 4. Implement all init methods and set self as delegate. 5. Implement all delegates, create ivars for all and call them if not nil.Tuesday, September 11, 12
  34. 34. ARC compatibility • Simple: #if ! __has_feature(objc_arc) #error This file must be compiled with ARC. Please add -fobjc-arc to the compiler flags of this file. #endif • Better: Use Nick Lockwood’s ARC Helper .h file: https://gist.github.com/1563325Tuesday, September 11, 12
  35. 35. Improving AVAudioPlayer #import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> #import "ARCHelper.h" typedef void (^AudioPlayerDidFinishPlayingBlock)(BOOL); typedef void (^AudioPlayerDecodeErrorDidOccurBlock)(NSError *); typedef void (^AudioPlayerBeginInterruptionBlock)(); typedef void (^AudioPlayerEndInterruptionBlock)(NSUInteger); @interface BlockAudioPlayer : AVAudioPlayer <AVAudioPlayerDelegate> @property (nonatomic, copy) AudioPlayerDidFinishPlayingBlock audioPlayerDidFinishPlayingBlock; @property (nonatomic, copy) AudioPlayerDecodeErrorDidOccurBlock audioPlayerDecodeErrorDidOccurBlock; @property (nonatomic, copy) AudioPlayerBeginInterruptionBlock audioPlayerBeginInterruptionBlock; @property (nonatomic, copy) AudioPlayerEndInterruptionBlock audioPlayerEndInterruptionBlock; @endTuesday, September 11, 12
  36. 36. Improving AVAudioPlayer - (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError { self = [super initWithContentsOfURL:url error:outError]; if (self) { self.delegate = self; } return self; } #pragma mark - AVAudioPlayerDelegate - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { if (audioPlayerDidFinishPlayingBlock) audioPlayerDidFinishPlayingBlock(flag); } https://github.com/gpambrozio/BlockAudioPlayer Zachary Waldowski’s https://github.com/zwaldowski/BlocksKitTuesday, September 11, 12
  37. 37. Hate comes back with a new faceTuesday, September 11, 12
  38. 38. Hate comes back with a new faceTuesday, September 11, 12
  39. 39. Hate comes back with a new faceTuesday, September 11, 12
  40. 40. A vision on how to end this hatredTuesday, September 11, 12
  41. 41. A vision on how to end this hatredTuesday, September 11, 12
  42. 42. A vision on how to end this hatredTuesday, September 11, 12
  43. 43. A vision on how to end this hatredTuesday, September 11, 12
  44. 44. Striking hatred with codeTuesday, September 11, 12
  45. 45. Striking hatred with codeTuesday, September 11, 12
  46. 46. Striking hatred with code UIWindow UView UILabel UIButtonTuesday, September 11, 12
  47. 47. Striking hatred with code UIWindow UView UILabel UIButtonTuesday, September 11, 12
  48. 48. Striking hatred with code UIWindow UView UILabel UIButtonTuesday, September 11, 12
  49. 49. Lesson number 2 2. UIWindow is an unkown obscure class that’s very rarely used (and with very poor documentation) but that might solve some tricky UI problems. Use with care.Tuesday, September 11, 12
  50. 50. UIWindow basics • No hierarchy. UIWindow are ordered using their windowLevel: • UIWindowLevelNormal • UIWIndowLevelAlert • UIWindowLevelStatusBar • If on the same level, ordered by creation time, last on top. • makeKeyAndVisible can push it to the top, but still respecting windowLevel.Tuesday, September 11, 12
  51. 51. Key Window • From the docs: “...window which is currently receiving keyboard events and other non touch-related events. Whereas touch events are delivered to the window in which the touch occurred, events that do not have an associated coordinate value are delivered to the key window of your application. Only one window at a time may be key." • When you create a UIWindow and call makeKeyWindow, don’t just call resignKeyWindow. Save the previous keyWindow and call makeKeyWindow on it when you’re done.Tuesday, September 11, 12
  52. 52. Destroying hateTuesday, September 11, 12
  53. 53. Lessons number 3 and 4 3. Open your mind to other libraries and don’t assume you need to do it the Apple wayTuesday, September 11, 12
  54. 54. Lessons number 3 and 4 3. Open your mind to other libraries and don’t assume you need to do it the Apple way 4. Don’t be lazy. It’s fun to implement something from scratch.Tuesday, September 11, 12
  55. 55. Spreading the love • BlockAlertView and BlockActionSheets are open source • github.com/gpambrozio/BlockAlertsAnd- ActionSheetsTuesday, September 11, 12
  56. 56. Spreading the love • BlockAlertView and BlockActionSheets are open source • github.com/gpambrozio/BlockAlertsAnd- ActionSheets • blog.codecropper.com/2012/01/replicating- tweetbot-alerts-and-action-sheets/Tuesday, September 11, 12
  57. 57. Spreading the love • BlockAlertView and BlockActionSheets are open source • github.com/gpambrozio/BlockAlertsAnd- ActionSheets • blog.codecropper.com/2012/01/replicating- tweetbot-alerts-and-action-sheets/ • Improvements to UIAlertView and UIActionSheetTuesday, September 11, 12
  58. 58. Spreading the love • Import 6 files, 3 .h and 3 .mTuesday, September 11, 12
  59. 59. Spreading the love • Import 6 files, 3 .h and 3 .m • Copy 1 .h file with UI attributesTuesday, September 11, 12
  60. 60. Spreading the love • Import 6 files, 3 .h and 3 .m • Copy 1 .h file with UI attributes • Copy PNG assets or create your ownTuesday, September 11, 12
  61. 61. Spreading the love • Import 6 files, 3 .h and 3 .m • Copy 1 .h file with UI attributes • Copy PNG assets or create your own • Change UI (optional)Tuesday, September 11, 12
  62. 62. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view setDestructiveButtonWithTitle:@”Delete”, block:^{ [self removeItem]; }]; [view show]; }Tuesday, September 11, 12
  63. 63. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view setDestructiveButtonWithTitle:@”Delete”, block:^{ [self removeItem]; }]; [view show]; }Tuesday, September 11, 12
  64. 64. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view setDestructiveButtonWithTitle:@”Delete”, block:^{ [self removeItem]; }]; [view show]; }Tuesday, September 11, 12
  65. 65. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view setDestructiveButtonWithTitle:@”Delete”, block:^{ [self removeItem]; }]; [view show]; }Tuesday, September 11, 12
  66. 66. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view setDestructiveButtonWithTitle:@”Delete”, block:^{ [self removeItem]; }]; [view show]; }Tuesday, September 11, 12
  67. 67. - (IBAction)sendItem:(id)sender { BlockAlertView *view = [BlockAlertView alertWithTitle:@”Send to” message:@”Send message using:”]; [view setCancelButtonWithTitle:@”Cancel” block:nil]; [view addButtonWithTitle:@”Twitter”, block:^{ [self sendWith:kTwitter]; }]; [view addButtonWithTitle:@”Facebook”, block:^{ [self sendWith:kFacebook]; }]; [view setDestructiveButtonWithTitle:@”Delete”, block:^{ [self removeItem]; }]; [view show]; }Tuesday, September 11, 12
  68. 68. Spreading the loveTuesday, September 11, 12
  69. 69. Spreading the loveTuesday, September 11, 12
  70. 70. Advanced love • Background and buttons and simple PNGsTuesday, September 11, 12
  71. 71. Advanced love • It’s only an UIView.... [BlockBackground sharedInstance] UIViewTuesday, September 11, 12
  72. 72. Advanced love • BlockTextPromptAlertViewTuesday, September 11, 12
  73. 73. Animated loveTuesday, September 11, 12
  74. 74. Animated loveTuesday, September 11, 12
  75. 75. Animated loveTuesday, September 11, 12
  76. 76. Animated loveTuesday, September 11, 12
  77. 77. Animated loveTuesday, September 11, 12
  78. 78. Animated loveTuesday, September 11, 12
  79. 79. Animated loveTuesday, September 11, 12
  80. 80. Lesson number 5 5. Opening your source is good: Improves your karma, your reputation and your source code.Tuesday, September 11, 12
  81. 81. Gustavo Ambrozio • @gpambrozio • blog.codecropper.com • github.com/gpambrozio • linkedin.com/in/gustavoambrozioTuesday, September 11, 12
  82. 82. We’re hiring 2 apps in top 25 grossing appsTuesday, September 11, 12

×