Reader Submissions - New Year's 2014
As we prepare to increment our NSDate
by 1
, it’s time once again for NSHipster end-of-the-year Reader Submissions! Last year, we got some mind-blowing tips and tricks. With the release of iOS 7 & Mavericks, and a year’s worth of new developments in the Objective-C ecosystem, there was a ton of new stuff to write about.
Thanks to Arnaud Coomans, Cédric Luthi, David Grandinetti, Ell Neal, Eric Allam, Erik Kerber, Jim Kubicek, Joachim Bengtsson, Johannes Lund, Josh Avant, João Prado Maia, Justin R. Miller, Kamil Pyć, Matthew Teece, Maximilian Tagher, Nigel Timothy Barber, Nolan O’Brien, Pitiphong Phongpattranont, Steve Moser, Thomas Visser, Vadim Shpakovski, & @jurre for contributing their great tips.
GCC Code Block Evaluation C Extension
Let’s make this official: NSHipster’s Objective-C trend of 2013 is code block evaluation assignment. Recommended by both Jim Kubicek and Maximilian Tagher (citing this blog post by Dominik Wagner), this trick does wonders to make code cleaner, safer, and more concise.
Behind the magic is a GCC C extension, which causes a code block to return a value if enclosed within brackets and parentheses.
Watch, as it cuts through this view controller code like butter!
self.search Bar = ({
UISearch Bar *search Bar = [[UISearch Bar alloc] init With Frame:({
CGRect frame = self.table View.frame;
frame.size.height = 50.0f;
frame;
})];
search Bar.delegate = self;
search Bar;
});
This not only segregates configuration details into initialization, but the additional scope allows generic variable names like frame
, button
, and view
to be reused in subsequent initializations. No more login
!
If code craftsmanship is important to you, strongly consider making this standard practice in your work. It may look a bit weird at first, but this will very likely become common convention by the end of 2014.
?:
Default Values with GNU-style Ternary The ternary operator, ?
, is shorthand for if () {...} else {...}
. However, because of how difficult it can be to understand statements with ternary operators at a glance, they are generally dispreferred by veteran coders.
Nonetheless, Maximilian Tagher offers a lesser-known (yet much-loved by those in-the-know) use of the ternary operator: ?:
, which acts as a convenient way to specify a fallback value to return if the left-hand side is nil
.
NSLog(@"%@", @"a" ?: @"b"); // @"a"
NSLog(@"%@", nil ?: @"b"); // @"b"
This is especially convenient for providing default behavior when a property may not be set:
dispatch_async(self.completion Queue ?: dispatch_get_main_queue(), ^{
…
});
The main downside of this approach is that default Xcode project warning settings will raise a warning. You can get around this by wrapping the relevant code block in #pragma
declarations, but the added LOC nullifies much of the brevity that this approach provides:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
self.name = name ?: @"Unknown";
#pragma clang diagnostic pop
@import
Vadim Shpakovski reminds us of one of the more obscure additions to LLVM 5.0, the @import
keyword. No more are the days of Xcode ▹ Project ▹ TARGETS ▹ General ▹ Linked Frameworks and Libraries ▹ +
. With @import
, the Xcode will automatically link Map
, Core
, or any other referenced framework as necessary. Even Prefix.pch
benefits, sporting a svelte new physique:
@import UIKit;
@import Foundation;
@import Core Graphics;
Customizing MKMapView Tiles
Justin R. Miller, of MapBox fame, brings to our attention what is perhaps the most important feature in iOS 7 mapping: custom tile overlays:
Relatively little-known feature of iOS 7: you can turn off Apple’s own maps now with
MKTile
,Overlay MKTile
, andOverlay Renderer can
, unlike you could before with Apple or (pre-iOS 6) Google. And check out MBXMapKit if you’d like to do it in one line of code.Replace Map Content
Thoughtbot’s Blog Post on MapKit Performance
Speaking of iOS maps, João Prado Maia cites an amazing blog post by thoughtbot, “How To Efficiently Display Large Amounts of Data on iOS Maps” by Theodore Calmes. Consider it a must-read if you plan to do any significant iCartography in 2014.
NSAttributedString + HTML in iOS 7
Shifting gears a little bit, Eric Allam remarks that NSAttributed
can do HTML now in iOS 7 with the new NSHTMLText
document type attribute. Combine with MMMarkdown for ridiculously easy Markdown rendering in a UIText
.
Launch Arguments & User Defaults
Offering another tip, Vadim Shpakovski calls our attention to the relationship between launch arguments and NSUser
:
The command line argument -Test
can be checked in code with [[NSUser
. This is useful for debugging development builds.
UIView Animation Curve
Value
Woes of a Missing Thomas Visser bemoans his pick for least awesome Objective-C development in 2013:
In iOS7, the animation of the keyboard show/hide changed. Its duration and, most notably, its curve is different from previous iOS versions. As before, if you want to animate something with the keyboard, you can listen to
UIKeyboard
/Will Show Notification UIKeyboard
and use the values from theWill Hide Notification user
dictionary to coordinate your animations with the keyboard. TheInfo user
dictionary contains the keyboard’s begin frame, end frame, animation curve and animation duration.Info
However, in iOS 7, the animation curve is an undefined value, meaning that it is not one of the 4 defined values of
UIView
. Instead, its value isAnimation Curve 7
. This is a problem if you want to use the same curve in your own animation, because such a curve is not defined. The work-around, as discussed on the Apple forums, is to manually translate theUIView
to aAnimation Curve UIView
value. From the definition ofAnimation Options UIView
, we learn that this translation is done by shifting the curve value 16 times to the left:Animation Options option = curve << 16
. This works great, but shouldn’t be necessary. I hope Apple will add this mysterious 5th curve to the definitions in a future iOS update.
Injecting Analytics on Outgoing Links
David Grandinetti has a tip for apps that want to track outgoing links from within an app: override App
:
-(BOOL)open URL:(NSURL *)url{
if ([[url scheme] has Prefix:@"http"]) {
[[GAI shared Instance].default Tracker send View:[url absolute String]];
}
return [super open URL:url];
}
In this example, this information is being sent to Google Analytics, but one could easily adapt this approach for any analytics provider.
Reviving the Look-and-Feel of iOS 6 in Your App with One Weird Trick (UI Designers hate it!)
Still resisting the aesthetics of this year’s iOS makeover? Kamil Pyć shows us how to act as if iOS 7 never happened:
[[NSUser Defaults standard User Defaults] set Object:@YES for Key:@"UIUse Legacy UI"]
And, of course, following up from a previous tip, since
NSUser
is tied to launch arguments, this can also be specified on launch. Keep this tucked in the back of your mind—this could make for a simple yet effective April Fool’s joke.Defaults
Detecting Category Method Collisions
Categories are great, but suffer from that same original sin of Objective-C: lack of name-spacing. Duplicate method declarations in categories interact in undefined ways, and may lead to difficult-to-debug behavior.
Fortunately, Cédric Luthi shows us how to tell if any category methods are getting up in one another’s business:
Set the
OBJC_PRINT_REPLACED_METHODS
environment variable toYES
in order to automatically log all methods that are smashed by categories.
A .plist of Emoji, Grouped by Category
In an encore submission, Cédric brings us a .plist file of Emoji grouped by category (mirrored from CloudApp to Gist in order to be more searchable). 😄👍
Quickly Determining the Type of Image Data
Here’s a simple function from Nolan O’Brien that can be used to determine the type of image data based on the first couple bytes of the header:
static inline NSPUIImage Type NSPUIImage Type From Data(NSData *image Data) {
if (image Data.length > 4) {
const unsigned char * bytes = [image Data bytes];
if (bytes[0] == 0xff &&
bytes[1] == 0xd8 &&
bytes[2] == 0xff)
{
return NSPUIImage Type_JPEG;
}
if (bytes[0] == 0x89 &&
bytes[1] == 0x50 &&
bytes[2] == 0x4e &&
bytes[3] == 0x47)
{
return NSPUIImage Type_PNG;
}
}
return NSPUIImage Type_Unknown;
}
Print KVO Context
Once again, showing off his unmatched knowledge of Objective-C internals, Cédric shares this extremely useful tip for debugging Key-Value Observing.
Print which context is passed to
observe
in lldb.Value For Key Path:of Object:change:context:
Say you have declared a context like this:
static const void *My Foo Context = &My Foo Context;
…and you want to to know what context it is when you are inside
- (void)observe Value For Key Path:(NSString *)key Path
of Object:(id)object
change:(NSDictionary *)change
context:(void *)context
You can do this:
(lldb) image lookup -a `context`
Address: My App[0x00026258] (My App.__DATA.__data + 4)
Summary: My Foo Context
Creating a KeyPath from Selectors
On the subject of Key-Value Coding, Pitiphong Phongpattranont offers this useful function that builds a keypath from a variable list of selectors:
inline NSString * PTPKey Path For Selectors(SEL selector, ...) {
if (!selector) {
return nil;
}
NSMutable Array *selectors = [NSMutable Array array];
va_list args;
va_start(args, selector);
SEL arg = selector;
do {
[selectors add Object:NSString From Selector(arg)];
} while((arg = va_arg(args, SEL)));
va_end(args);
return [selectors components Joined By String:@"."];
}
NSString *key Path = PTPKey Path For Selectors(@selector(data), @selector(name), nil);
// => @"data.name"
Nomad CLI Utilities
And finally, Matthew Teece gives a shout-out to Nomad, a world-class collection of command-line utilities—specifically, Houston, which can send and manage push notifications from the command line, or within your Ruby application.
$ apn push "<token>" -c /path/to/cert.pem -m "Hello!"
Thus concludes this year’s reader submissions. Thanks again to everyone for your submissions!
And thanks to you, dear reader, for sticking with NSHipster for another 52 weeks. Between the WWDC session and the book, 2013 has been a bellwether year for NSHipster. Thank you for your support and enthusiasm for the site—it really does mean the world to me.
We have a ton of great stuff planned for 2014, so keep your fixies primed and your artisanal espresso hot for another season of great iOS and OS X knowledge.