Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

Latest commit

 

History

History
257 lines (173 loc) · 13.5 KB

2014-01-01-new-years-2014.md

File metadata and controls

257 lines (173 loc) · 13.5 KB
title author category excerpt status
Reader Submissions -<br/> New Year's 2014
Mattt
Reader Submissions
As we prepare to increment our NSDateComponents -year by 1, it's time once again for NSHipster end-of-the-year Reader Submissions!
swift
n/a

As we prepare to increment our NSDateComponents -year 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.searchBar = ({
    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:({
        CGRect frame = self.tableView.frame;
        frame.size.height = 50.0f;
        frame;
    })];
    searchBar.delegate = self;
    searchBar;
});

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 loginButtonFrame = ... / signupButtonFrame = ...!

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.completionQueue ?: 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 MapKit, CoreData, or any other referenced framework as necessary. Even Prefix.pch benefits, sporting a svelte new physique:

@import UIKit;
@import Foundation;
@import CoreGraphics;

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 MKTileOverlay, MKTileOverlayRenderer, and canReplaceMapContent, 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.

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 NSAttributedString can do HTML now in iOS 7 with the new NSHTMLTextDocumentType document type attribute. Combine with MMMarkdown for ridiculously easy Markdown rendering in a UITextView.


Launch Arguments & User Defaults

Offering another tip, Vadim Shpakovski calls our attention to the relationship between launch arguments and NSUserDefaults:

The command line argument -TestFeatureEnabled YES can be checked in code with [[NSUserDefaults standardUserDefaults] boolForKey:@"TestFeatureEnabled"]. This is useful for debugging development builds.


Woes of a Missing UIViewAnimationCurve Value

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 UIKeyboardWillShowNotification/UIKeyboardWillHideNotification and use the values from the userInfo dictionary to coordinate your animations with the keyboard. The userInfo dictionary contains the keyboard's begin frame, end frame, animation curve and animation duration.

However, in iOS 7, the animation curve is an undefined value, meaning that it is not one of the 4 defined values of UIViewAnimationCurve. Instead, its value is 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 the UIViewAnimationCurve to a UIViewAnimationOptions value. From the definition of UIViewAnimationOptions, we learn that this translation is done by shifting the curve value 16 times to the left: 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 AppDelegate -openURL::

-(BOOL)openURL:(NSURL *)url{
    if ([[url scheme] hasPrefix:@"http"]) {
        [[GAI sharedInstance].defaultTracker sendView:[url absoluteString]];
    }
    return [super openURL: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:

[[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"UIUseLegacyUI"]

And, of course, following up from a previous tip, since NSUserDefaults 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.


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 to YES 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 NSPUIImageType NSPUIImageTypeFromData(NSData *imageData) {
    if (imageData.length > 4) {
        const unsigned char * bytes = [imageData bytes];

        if (bytes[0] == 0xff &&
            bytes[1] == 0xd8 &&
            bytes[2] == 0xff)
        {
            return NSPUIImageType_JPEG;
        }

        if (bytes[0] == 0x89 &&
            bytes[1] == 0x50 &&
            bytes[2] == 0x4e &&
            bytes[3] == 0x47)
        {
            return NSPUIImageType_PNG;
        }
    }

    return NSPUIImageType_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 observeValueForKeyPath:ofObject:change:context: in lldb.

Say you have declared a context like this:

static const void *MyFooContext = &MyFooContext;

...and you want to to know what context it is when you are inside

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context

You can do this:

(lldb) image lookup -a `context`
      Address: MyApp[0x00026258] (MyApp.__DATA.__data + 4)
      Summary: MyFooContext

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 * PTPKeyPathForSelectors(SEL selector, ...) {
  if (!selector) {
    return nil;
  }

  NSMutableArray *selectors = [NSMutableArray array];
  va_list args;
  va_start(args, selector);
  SEL arg = selector;
  do {
    [selectors addObject:NSStringFromSelector(arg)];
  } while((arg = va_arg(args, SEL)));
  va_end(args);

  return [selectors componentsJoinedByString:@"."];
}
NSString *keyPath = PTPKeyPathForSelectors(@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.