Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
NSHipster New Year's 2015

Season's Greetings, NSHipsters!

As the year winds down, and we take a moment to reflect on our experiences over the past months, one thing is clear: 2014 has been an incredible year professionally for Apple developers. So much has happened in such a short timespan, and yet it's hard to remember our relationship to Objective-C before Swift, or what APIs could have captivated our imagination as much as iOS 8 or WatchKit.

It's an NSHipster tradition to ask you, dear readers, to send in your favorite tips and tricks from the past year for publication over the New Year's holiday. This year, with the deluge of new developments—both from Cupertino and the community at large—there should be no shortage of interesting tidbits to share.

Submit your favorite piece of Swift or Objective-C trivia, framework arcana, hidden Xcode feature, or anything else you think is cool, and you could have it featured in the year-end blowout article. Just comment on this gist below!

If you're wondering about what to post, look to past year's write-ups for inspiration (2014, 2013).

I can't wait to see what you all send in!

@bitops
Copy link

bitops commented Dec 1, 2014

Shameless plug for some Array extensions I wrote to mimic some of the popular methods from Ruby's Enumerable module. Attributions, where applicable, are in the source. https://github.com/bitops/swift-collections/blob/master/SwiftCollections.swift

@defagos
Copy link

defagos commented Dec 1, 2014

Instead of if-testing a block against nil before calling it:

if (block) {
    result = block(param1, param2);
}
else {
    result = defaultValue;
}

if love using the one-liner:

result = block ? block(param1, param2) : defaultValue;

to declutter my code. For blocks returning void, simply use nil as default value:

voidBlock ? voidBlock(param1, param2) : nil;

@defagos
Copy link

defagos commented Dec 1, 2014

Given the fact that literals are most of the time associated with numbers and collections, I often forget that they work for UTF8-encoded NULL-terminated C-strings as well, especially when I write code using the runtime:

NSString *propertyAttributesString = @(property_getAttributes(class_getProperty([NSObject class], "description")));         // T@"NSString",R,C

It is namely quite amusing that:

[@("someString") isEqualToString:@"someString"] == YES

is something I so easily forget, especially since this boxing syntax is totally natural.

@cmyr
Copy link

cmyr commented Dec 1, 2014

Optionals should be avoided. Implicitly unwrapped optionals should be strongly avoided. Want to declare a var but don't necessarily have an initial value at init time? Use the lazy keyword, and just don't call the getter before you have your real value.

lazy var someModelStructure = ExpensiveClass()

if you call set on this var without having ever called the getter, the lazy expression is never evaluated. Great for references to views that you don't necessarily want to init until viewDidLoad, for instance.

@incanus
Copy link

incanus commented Dec 4, 2014

Now that 10.10 is allowing easy screencasts of iOS devices (which itself is a neat tip), I find myself again using a framework I made some time back for showing touches on screen: Fingertips. Great for presentations, too.

@kazmasaurus
Copy link

You can optional chain calling a closure just like you can accessing a subscript

func foo(completion: ((String) -> Void)?) {
    completion?("Heyo!!") ?? println("Oyeh!!")
}

foo { println($0) }     // prints "Heyo"
foo(nil)                // prints "Oyeh"

@0xced
Copy link

0xced commented Dec 9, 2014

Sometimes, you need to know which language your app is running in. Often, people will use +[NSLocale preferredLanguages]. Unfortunately this tells nothing about the language the app is actually displaying. It will just give you the ordered list as found in Settings → General → Language & Region → Preferred Language Order on iOS or System Preferences → Language & Region → Preferred Languages on OS X.

Imagine that the preferred language order is {English, French} but your app is German only. Calling [[NSLocale preferredLanguages] firstObject] will give you English when you want German.

The proper way to get the actual language used by the app is to use [[NSBundle mainBundle] preferredLocalizations].

From the documentation:

An array of NSString objects containing language IDs for localizations in the bundle. The strings are ordered according to the user's language preferences and available localizations.

From a comment in NSBundle.h:

a subset of this bundle's localizations, re-ordered into the preferred order for this process's current execution environment; the main bundle's preferred localizations indicate the language (of text) the user is most likely seeing in the UI

You may also need to use +[NSLocale canonicalLanguageIdentifierFromString:] in order to ensure a canonical language identifier.

@samirGuerdah
Copy link

Functional Programming vs OOP vs Imperative Programming

@dodikk
Copy link

dodikk commented Dec 9, 2014

@samirGuerdah vs "Playground Driven Development" )))

@fpillet
Copy link

fpillet commented Dec 9, 2014

When you have objects in a NSArray, you can extract another NSArray with the value returned from a selector applied to each object using -valueForKey. This call will turn the string into a @selector if it doesn't find a property with the same name, and call the selector:

NSArray *array = <some array of NSObject> ;
NSArray *descriptions = [array valueForKey:@"description"];

The cool thing is that this works with any selector you define on your objects.

This is part of the key-value coding conventions. Relevant documentation for NSArray is here

@hborders
Copy link

hborders commented Dec 9, 2014

If you're repeatedly debugging the same problem over and over, you can run your app without rebuilding by using <ctrl>+<cmd>+r.

@jonfriskics
Copy link

Swift Playgrounds all share the same Shared Playground Data folder that's symlinked to /Users/HOME/Documents/Shared Playground Data.

If you like using lots of Playgrounds, you'll want to organize the data that each Playground is using into subfolders of that shared folder, but then you've got to let the Playground know where to look. Here's a helper function that I use that makes that easy:

func pathToFileInSharedSubfolder(file: String) -> String {
    return XCPSharedDataDirectoryPath + "/" + NSProcessInfo.processInfo().processName + "/" + file
}

That processName property in NSProcessInfo contains the name of the Playground file, so as long as you have already created a sub-folder in the Shared Playground Data folder with the same name you can access those files pretty easily, like reading local JSON:

var jsonReadError:NSError?
let jsonData = NSFileManager.defaultManager().contentsAtPath(pathToFileInSharedSubfolder("data.json"))!
let jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &jsonReadError) as [AnyObject]

or pulling out a local image:

let imageView = UIImageView()
imageView.image = UIImage(contentsOfFile: pathToFileInSharedSubfolder("image.png"))

@Fl0p
Copy link

Fl0p commented Dec 10, 2014

Cocoapods
@orta 😉

@palewar
Copy link

palewar commented Dec 16, 2014

Playground as Documentation.

And also Firefox coming to iOS and looks to be mostly done in Swift. It's still early days and a great opportunity for community to get involved with the development. Contributing guidelines are non existent and so are code comments, making it difficult to get involved. If somebody can do some digging and come up with some simple guidelines and code comments for existing code, it will be great for everybody.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment