Pod5 Podcast Draft Hidden Sticky

Posted Tuesday, 15 April 2014 | Post Comment |

Orta and I are recording episode 3 of Pod5, a new bite-sized podcast about new Objective-C pods. It’s super fun.

image

Subscribe on iTunes, check out http://pod5.io and follow @pod5podcast.

 

Reorganizing CocoaPod Folder Structure for Unit and Integration Testing Draft Hidden Sticky

Posted Wednesday, 26 March 2014 | Post Comment |

My first CocoaPod described in this post sparked a discussion with the CocoaPods team. There’re a few things I didn’t like about the project organization.

  1. The name of the workspace was “Demo”. I would expect the project to have the same name as the pod being written.
  2. The demo project contained the Podfile, requiring developers to cd into Demo, then run pod install. I would expect the Podfile to live in the root of the project.
  3. The demo project contained tests for the pod. I would expect the demo to be a demo and to have a test project that unit tests the pod being written.

All these issues can be addressed. I’ve reorganized two of my pods, ARASCIIImageSwizzle and ARTiledImageView and fixed all the issues above.

Folder Structure

Fixing the folder structure and naming is fairly straightforward. You can move the contents of the Demo folder one level up. This brings the Podfile to the root. The Podfile can also explicitly name the workspace.

  1. workspace 'ARASCIISwizzle'
  2.  
  3. pod 'ARASCIISwizzle', :path => 'ARASCIISwizzle.podspec'
  4.  
  5. xcodeproj 'Demo.xcodeproj'

You’ll have to update .travis.yml to build a different workspace and to reference the development pod in the same folder.

  1. language: objective-c
  2. before_install:
  3.   - brew update
  4.   - brew uninstall xctool
  5.   - brew install xctool
  6.   - export LANG=en_US.UTF-8
  7.   - gem i cocoapods --no-ri --no-rdoc
  8.   - pod install
  9. xcode_workspace: ARASCIISwizzle.xcworkspace
  10. xcode_scheme: Demo
  11. xcode_sdk: iphonesimulator

You can see this commit in ARTiledImageView that accomplishes the same.

Integration Tests

The tests that are part of the Demo project are really integration tests. I’ve renamed those consequently with a bulk replace of file names and within the source code. You can see it in this commit in ARTiledImageView.

Unit Tests

Lets build a clean unit test project for the pod, independent of the Demo project.

Create a new empty project in XCode called “Tests” or, if you prefer, “UnitTests”.

Add a target.

Screen Shot 2014-03-25 at 2.15.29 PM

 

Choose Cocoa Touch Unit Testing bundle.

Screen Shot 2014-03-25 at 2.15.48 PM

Open Manage Schemes and make sure the scheme is Shared. Then edit the Test project’s scheme and check the Run box under the Build action.

Screen Shot 2014-03-26 at 9.58.04 AM

The Podfile must now reference “Demo”, “Tests” and the “IntegrationTests” project. The syntax for that is a bit backwards, discussed in https://github.com/CocoaPods/CocoaPods/issues/1922.

  1. workspace 'ARASCIISwizzle'
  2.  
  3. pod 'ARASCIISwizzle', :path => 'ARASCIISwizzle.podspec'
  4.  
  5. xcodeproj 'Demo.xcodeproj'
  6.  
  7. target 'Demo' do
  8.   pod 'FLKAutoLayout', '~> 0.1.1'
  9.   xcodeproj 'Demo.xcodeproj'
  10. end
  11.  
  12. target 'IntegrationTests' do
  13.   pod 'Specta', '~> 0.2.1'
  14.   pod 'Expecta', '~> 0.2.3'
  15.   pod 'FBSnapshotTestCase', :head
  16.   pod 'EXPMatchers+FBSnapshotTest', :head
  17.   xcodeproj 'Demo.xcodeproj'
  18. end
  19.  
  20. target 'Tests' do
  21.   pod 'Specta', '~> 0.2.1'
  22.   pod 'Expecta', '~> 0.2.3'
  23.   pod 'FBSnapshotTestCase', :head
  24.   pod 'EXPMatchers+FBSnapshotTest', :head
  25.   pod 'OCMock', '~> 2.2.3'
  26.   xcodeproj 'Tests.xcodeproj'
  27. end

Travis will build “Tests” and “IntegrationTests” in a matrix.

  1. xcode_scheme:
  2.   - Demo
  3.   - Tests

You can see the results of the above in this commit in ARASCIISwizzle.

Conclusion

This is a lot closer to the Rubygems-style organization where everything is accessible from the top and tests live at the same level as pod classes.

 

Mapping and Tiling on iOS Draft Hidden Sticky

Posted Wednesday, 19 March 2014 | Post Comment |

I’ve learned a great deal about iOS UI in the past couple of months, mostly working on the Artsy Mobile app and the in-Fair experience for the New York Armory Show. I feel incredibly lucky to have Orta from CocoaPods fame sitting a few feet away and being a patient mentor. We’ve built a map that, unlike many similar indoor maps, actually doesn’t suck. Huge kudos to KatarinaBatina, who did a ton of design work and everybody else at Artsy who has contributed boatloads of client and server-side code to make this happen.

A few open-source projects came out of this. I’ve extracted the image zoom portion into ARTiledImageView. It’s capable of displaying really large images at multiple zoom levels that you can tile with another really simple command-line tool, dzt. This has been long available on the web OpenSeaDragon, and is now at your fingertips on iOS. The second library is NAMapKit. It’s a really simple framework that combines an image and map annotations. We’ve hacked on it heavily and I’ve spent a couple of weeks refactoring its code to enable a clean way of supporting multiple map types, including ones backed by tiled images. Finally, shout out to ios-snapshot-test-case and ios-snapshot-test-case-expecta, which make testing of all this UI stuff a breeze.

 

 

Your First CocoaPod Draft Hidden Sticky

Posted Sunday, 02 March 2014 | Post Comment |

Right behind Your First Ruby Gem, here’s a walkthrough of creating your first Objective-C CocoaPod. Now, I must admit that after only two months of Objective-C programming, I am not nearly an expert. So I enlisted Orta from CocoaPods to stand behind my back and poke me with a stick whenever I did something wrong. It’s also important to note that there’s a lot of development going on in CocoaPods and my walkthrough represents the current state of affairs as of February 2014.

TL;DR

This was really easy. Open-source in Objective-C FTW!

I wrote objc-ngram, a small n-gram library. For example, you could use it as a rudimentary full text search engine.

image

Create a Pod

Get CocoaPods by running gem install cocoapods. Create a pod.

  1. $ pod lib create objc-ngram
  2. Creating `objc-ngram` Pod
  3. Configuring template
  4.  
  5. To learn more about the template see `https://github.com/CocoaPods/pod-template`.
  6. To learn more about creating a new pod, see `http://guides.cocoapods.org/making/making-a-cocoapod`.

This creates an objc-ngram folder with a bunch of files. As of now, it’s missing a Gemfile, that I have created manually.

  1. source 'https://rubygems.org'
  2.  
  3. gem 'cocoapods'

Run bundle install, which will generate a Gemfile.lock and a .bundle folder. We don’t intend to lock the version of CocoaPods, so add Gemfile.lock and .bundle to .gitignore.

Create a Demo/Test Project

Lets get rid of the default project created by pod and re-create one that will reference our pod as a development pod.

  1. $ rm -rf Project

Create a new iOS Single View Application project and place it into the folder created above (objc-ngram).

image

Inside the Demo folder, create a Podfile. Yes, we’re going to write tests, using Specta and Expecta.

  1. pod "objc-ngram", :path => "../objc-ngram.podspec"
  2.  
  3. target "DemoTests" do
  4.   pod 'Specta', '~> 0.2.1'
  5.   pod 'Expecta', '~> 0.2.3'
  6. end

Run pod install.

  1. Demo> $ pod install
  2.  
  3. Analyzing dependencies
  4. Fetching podspec for `objc-ngram` from `../objc-ngram.podspec`
  5. Downloading dependencies
  6. Installing Expecta (0.2.3)
  7. Installing Specta (0.2.1)
  8. Installing objc-ngram (0.1.0)
  9. Generating Pods project
  10. Integrating client project
  11.  
  12. [!] From now on use `Demo.xcworkspace`.


Add the Demo/Pods folder, which includes all the pods installed from pod install,  to .gitignore.

Reopen the newly generated Demo.xcworkspace from XCode. The project should build and run.

Housekeeping

This is a good opportunity to update README, CHANGELOG and LICENSE.

Create a repository on Github and push the code you have so far.

Continuous Integration

There’s no excuse not to have tests or continuous integration for an open-source project.

In XCode, open Manage Schemes and make the Demo scheme shared.

image

Create a .travis.yml file at the root of your project.

  1. language: objective-c
  2. before_script:
  3.   - export LANG=en_US.UTF-8
  4.   - cd Demo ; pod install ; cd ..
  5. xcode_workspace: Demo/Demo.xcworkspace
  6. xcode_scheme: Demo
  7. xcode_sdk: iphonesimulator

Enable the project to build in Travis-CI and add a build status icon to the README.

  1. [![Build Status](https://travis-ci.org/dblock/objc-ngram.png)](https://travis-ci.org/dblock/objc-ngram)

Code & Tests

Write code and tests. Code goes into the Classes folder. Tests go into the Demo project. Note, that in order for new class files to be visible to the project you must remember to rerun pod install. Pod will also enable ARC for those new files, automatically.

I always write tests and start with very obvious ones for each and every new class. This serves as a placeholder for more useful functionality.

  1. #define EXP_SHORTHAND
  2. #include <Specta/Specta.h>
  3. #include <Expecta/Expecta.h>
  4. #include <objc-ngram/OCNDictionary.h>
  5.  
  6. SpecBegin(OCNDictionarySpec)
  7.  
  8. __block OCNDictionary *dict = nil;
  9.  
  10. describe(@"default width", ^{
  11.     beforeEach(^{
  12.         dict = [OCNDictionary dictionary];
  13.     });
  14.     
  15.     it(@"creates an instance", ^{
  16.         expect(dict).toNot.beNil();
  17.     });
  18. });
  19.  
  20. SpecEnd

CocoaPods lets you easily try pods with pod try, so the Demo project should also do something useful and visually stimulating.

Releasing the Pod

Run pod lib lint and fix any issues.

Run pod spec lint and make sure the project version looks right. You should see an error about a tag missing.

  1. [!] Pod::Executable fetch origin tags/1.0.0 2>&1
  2. fatal: Couldn't find remote ref tags/1.0.0

Once ready, tag 1.0.0 and push it to Github.

  1. git tag 1.0.0
  2. git push --tags

Clone https://github.com/cocoapods/specs. Create a versioned folder under specs, eg. specs/dblock/objc-ngram/1.0.0. Place the .podspec file from your project there. Check that the version in .podspec matches the folder name. Create a branch, commit the change and push it to your fork. Make a pull request to https://github.com/CocoaPods/specs, mine was https://github.com/CocoaPods/Specs/pull/8606.

Test Drive

Once the pull request has been merged, try your pod.

  1. pod repo update
  2. pod try objc-ngram

Full source code for objc-ngram is in https://github.com/dblock/objc-ngram.

 

iOS UIView with an Image and Text with Dynamic Height Draft Hidden Sticky

Posted Monday, 03 February 2014 | Post Comment |

Last week I struggled with this rather basic problem of adjusting the height of a UIView dynamically.

TL;DR: the solution was to align the bottom edge of  with a control with the view.

image

We use a custom UIView with a fixed height of 120px to display a separator, an image and text on its right. We also use FLKAutoLayout, which is where all these nice alignment methods come from.

  1. - (void)displayText:(NSString *)text andImage:(NSString *)imageUrl
  2. {
  3.     UIView *separator = [[UIView alloc] init];
  4.     separator.backgroundColor = [UIColor colorWithWhite: 0xe5/255.f alpha:1];
  5.     [self addSubview:separator];
  6.     [separator constrainHeight:@"1"];
  7.     [separator constrainWidthToView:self predicate:nil];
  8.     [separator alignCenterXWithView:self predicate:nil];
  9.  
  10.     ARAspectRatioImageView *imageView = [[ARAspectRatioImageView alloc] init];
  11.     NSURL *imageUrl = [NSURL URLWithString:url];
  12.     [imageView setImageWithURL:imageUrl];
  13.     [self addSubview:imageView];
  14.     [imageView alignTopEdgeWithView:self predicate:@"20"];
  15.     [imageView alignLeadingEdgeWithView:self predicate:@"20"];
  16.     [imageView constrainWidth:@"80"];
  17.     [imageView setContentMode:UIViewContentModeScaleAspectFit];
  18.    
  19.     UILabel *labelView = [[UILabel alloc] init];
  20.     labelView.text = text;
  21.     [self addSubview:labelView];
  22.     [labelView alignTopEdgeWithView:imageView predicate:@"0"];
  23.     [labelView alignAttribute:NSLayoutAttributeLeading
  24.                   toAttribute:NSLayoutAttributeTrailing
  25.                        ofView:imageView predicate:@"20"];
  26.     [labelView alignTrailingEdgeWithView:self predicate:@"-20"];
  27. }
  28.  
  29. - (CGSize)intrinsicContentSize
  30. {
  31.     return CGSizeMake(UIViewNoIntrinsicMetric, 120);
  32. }

This is obviously problematic for tall images and cropping art is a major offense. How do we dynamically calculate the height of the view based on the image and/or text height? Since the image is loaded dynamically, are we going to have to write some pretty involved code to wait until its height is known? Do we need to auto-size the label and calculate its height after it wraps over multiple lines?

The answer is that we don’t need to do any of this. With auto-layout we need to attach the image and the text to the bottom of the containing view.

  1. [self alignBottomEdgeWithView:labelView predicate:@"10"];
  2. [self alignBottomEdgeWithView:imageView predicate:@"10"];

With this constraint auto-layout seems to be smart enough to pick the tallest control to calculate the height of the view.

image

 

Mocking UIAlertView with OCMock Draft Hidden Sticky

Posted Thursday, 16 January 2014 | Post Comment |

Lets mock a UIAlertView with OCMock. Here’s a simple alert:

  1. UIAlertView *alert = [[UIAlertView alloc]
  2.     initWithTitle:@"Title"
  3.     message:@"Please press a button."
  4.     delegate:...
  5.     cancelButtonTitle:@"Cancel"
  6.     otherButtonTitles:@"OK", nil];
  7.  
  8. [alert show];

And a test that ensures that the alert has been displayed.

  1. id mockAlertView = [OCMockObject mockForClass:[UIAlertView class]];
  2. [[[mockAlertView stub] andReturn:mockAlertView] alloc];
  3. (void)[[[mockAlertView expect] andReturn:mockAlertView]
  4.                            initWithTitle:@"Title"
  5.                                  message:@"Please press a button."
  6.                                 delegate:OCMOCK_ANY
  7.                        cancelButtonTitle:OCMOCK_ANY
  8.                        otherButtonTitles:OCMOCK_ANY, nil];
  9. [[mockAlertView expect] show];
  10.  
  11. // make the alert appear here
  12.  
  13. [mockAlertView verify];
  14. [mockAlertView stopMocking];

The only interesting thing here is that you need to stopMocking the class or other tests will still have the mock enabled and will expect show to be called, producing weird and seemingly unrelated errors.

Related to this – if you’re going to be performing actions based on UIAlertView button presses, I highly recommend UIAlertView+Blocks, which lets you write the callback inline with the view. I haven’t figured out how to test that, you can help me on StackOverflow.

 

Debugging XCTool’s NSInternalInconsistencyException: Failed while trying to gather build settings for your scheme Draft Hidden Sticky

Posted Wednesday, 15 January 2014 | Post Comment |

Took me a while to figure this one out, trying to add a Travis-CI build to https://github.com/dblock/ios-snapshot-test-case-expecta.

The project build succeeds locally. Also succeeds on a different machine with a clean checkout. But fails in Travis with an obscure error:

  1. xctool -workspace
  2.     FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemo.xcworkspace
  3.     -scheme FBSnapshotTestCaseDemo
  4.     -configuration Debug clean
  5. [Info] Loading settings for scheme 'FBSnapshotTestCaseDemo' ...
  6. 2014-01-15 06:34:35.661 xctool[2048:2807]
  7.     *** Assertion failure in -[XcodeSubjectInfo buildSettingsForATarget],
  8.     /tmp/xctool-WSKd/xctool-0.1.14/xctool/xctool/XcodeSubjectInfo.m:813
  9. 2014-01-15 06:34:35.727 xctool[2048:2807]
  10.     *** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
  11.     reason: 'Failed while trying to gather build settings for your scheme; tried with actions: build, test, analyze'
  12.  
  13. *** First throw call stack:
  14. (
  15.     0   CoreFoundation   07fff98bbcb06 __exceptionPreprocess + 198
  16.     1   libobjc.A.dylib  07fff8fa7f3f0 objc_exception_throw + 43
  17.     2   CoreFoundation   07fff98bbc948 +[NSException raise:format:arguments:] + 104
  18.     3   Foundation       07fff979634c2 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 189
  19.     4   xctool           00001041b7ccf -[XcodeSubjectInfo buildSettingsForATarget] + 548
  20.     5   xctool           00001041b89d1 -[XcodeSubjectInfo loadSubjectInfo] + 150
  21.     6   xctool           00001041adbe4 -[Options validateAndReturnXcodeSubjectInfo:errorMessage:] + 2521
  22.     7   xctool           00001041a5e67 -[XCTool run] + 1303
  23.     8   xctool           00001041a50eb main + 507
  24.     9   xctool           00001041a2f84 start + 52
  25.     10  ???              0000000000008 0 + 8
  26. )
  27. libc++abi.dylib: terminate called throwing an exception
  28. make: *** [clean] Abort trap: 6

It says: Failed while trying to gather build settings for your scheme; tried with actions: build, test, analyze.

After some digging through xctool’s source code I found that it tries to run the following command:

  1. SHOW_ONLY_BUILD_SETTINGS_FOR_FIRST_BUILDABLE=YES
  2. DYLD_INSERT_LIBRARIES=/usr/local/Cellar/xctool/0.1.14/libexec/lib/xcodebuild-fastsettings-shim.dylib
  3. /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild
  4. test
  5. -showBuildSettings
  6. -project FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemo.xcodeproj
  7. -scheme FBSnapshotTestCaseDemo

It’s just trying to collect build settings for the scheme specified.

Interestingly, this succeeds locally for me, but fails on Travis with the following error.

  1. xcodebuild: error: The project 'FBSnapshotTestCaseDemo' does not contain a scheme named 'FBSnapshotTestCaseDemo'

This is the real problem, the schemes are user-specific unless marked as shared in the project. Both of my machines have the same username, but Travis obviously has another. here

You can change this in Manage Schemes in XCode.

Screenshot 2014-01-15 09.58.11

Screenshot 2014-01-15 09.59.22

Opened https://github.com/facebook/xctool/issues/295 to improve the error messaging in XCTool.

And here’s a successful build of the ios-snapshot-test-case-expecta project on Travis.

 

dotNetInstaller 2.3 Released Draft Hidden Sticky

Posted Monday, 06 January 2014 | Post Comment |

dotNetINstaller 2.3 has been released.

This project is alive and well after a solid 10 years since its first public release in 2003.

Thanks to all the contributors!

 

American Horror Story and Entitlement in a Job Interview Draft Hidden Sticky

Posted Friday, 03 January 2014 | Post Comment |

“Wednesday night’s American Horror Story: Coven delivered an interesting theory on youth culture: Millennials are actually dead inside.” I’m quoting Bustle’s article, which quotes the show itself. “I am a millennial. Generation Y, born between the birth of aids and 9/11 give or take. They call us the Global Generation. We are known for our entitlement and narcissism. Some say it’s because we’re the first generation where every kid gets a trophy just for showing up.”

Why am I quoting this? In the recent months some non-trivial number of junior candidates have shown up to a second interview without really knowing what we actually do as a company. They have never been beyond the homepage of our website, have never even read the “about” section or landed on our Engineering blog. They have not even created an account on the website that offers you to create one the first time you visit.

They just showed up.

Small companies is where a lot of innovation happens and are much more selective about their junior candidates. We put tremendous effort into hiring and consider juniors a serious investment. Arriving without any preparation annihilates your chances. Unlike big corporations that hire armies of juniors, you are rarely judged on puzzles and whiteboard coding. You’re not a “brain on a stick”, to quote a big company manager. So, find out as much as you can from the website of the company you’re interviewing at, write down names and terms, then Google everything until you’re sure you’ve left no stone unturned. Finally, think about 3 smart and sufficiently varied questions that you would want to ask an Engineer, a Product Manager, a Business person and the CEO. These should demonstrate that you have done the prep work. Arrive with confidence and genuine desire to both do your best and learn something in the process.

I am, however, happy to say that the horror story mentioned above is a tiny minority. In the past few months I had the pleasure of interviewing some very smart kids. I am 37, so most were born the year I wrote my first commercial software. I felt that I was repeatedly talking to very bright individuals. I was inspired by the vast majority of these juniors and was thrilled to hear about their projects and aspirations. I have to also mention that I am extremely happy to see a growing number of minorities and women in their ranks, something that our industry has always struggled with.

I am excited about the future, let me know how I can help!

 

Ruby Enumerable Detect with Value Draft Hidden Sticky

Posted Saturday, 21 December 2013 | Post Comment |

Given an Enumerable, you can detect a value that matches a condition. But what if you want the result of the evaluation? You can now use Enumerable##detect_value from the enumerable-detect-value gem.

Consider an example where you have an expensive Geocoder.search operation and a list of addresses, two of which are fake. The function returns nil for a fake address. We would like to find the geo-location of the first real address.

  1. addresses = [
  2.   '221B Baker Street, London, UK', # Sherlock Holmes
  3.   '1428 Elm Street, Springwood, Ohio', # Nightmare on Elm Street
  4.   '350 5th Ave, New York, NY' # Empire State Building
  5. ]
  6.  
  7. first_real_address = addresses.detect do |address|
  8.   Geocoder.search(address)
  9. end
  10.  
  11. first_real_address # 350 5th Ave, New York, NY

We would now have to call Geocoder.search on first_real_address twice.

Instead, using detect_value you can return the geo-location of the first real address.

  1. first_geo_location = addresses.detect_value do |address|
  2.   Geocoder.search(address)
  3. end
  4.  
  5. first_geo_location # lat: 40.74830, lng: -73.98554

The implementation for detect_value is straightforward.

  1. module Enumerable
  2.   # Returns the first result of the block that is not nil.
  3.   def detect_value(&block)
  4.     each do |el|
  5.       result = yield el
  6.       return result if result
  7.     end
  8.     nil
  9.   end
  10. end

I don’t think this can this be done with the current Ruby standard library without introducing a temporary variable, and Enumerable##Lazy won’t help.

 
< | 1  2  3  4  5  6  7  8  9  10  11  ... >