Reader Submissions - New Year's 2016
With 2015 behind us and the new year begun, it’s time again for an NSHipster tradition: reader submissions! As in year’s past, this installment is chock full of tips and tricks that can help ease your days working with Xcode, Swift, and Objective-C.
Many thanks to Cédric Luthi, Josip Ćavar, Juraj Hilje, Kyle Van Essen, Luo Jie, Mathew Huusko V, Nicolás Jakubowski, Nolan O’Brien, Orta Therox, Ray Fix, Stephen Celis, Taylor Franklin, Ursu Dan, Matthew Flint, @biggercoffee, and @vlat456 for their contributions!
defer
in Objective-C
Swift’s From Nolan O’Brien:
With the excellent addition of
defer
to Swift we Objective-C holdouts can’t help but feel envious of the improvements happening so rapidly to the Swift language. Until Apple officially adds@defer
devs can actually implement defer support simply enough with a macro in Objective-C. Below I’ve outlined how one can go about doing adefer
today in Objective-C. Personally, having Apple add@defer
seem like an easy win for Objective-C, but we’ll see what happens. :)
Nolan’s macro uses the GCC (cleanup())
attribute to execute a block when scope exits:
// some helper declarations
#define _nob_macro_concat(a, b) a##b
#define nob_macro_concat(a, b) _nob_macro_concat(a, b)
typedef void(^nob_defer_block_t)();
NS_INLINE void nob_defer Func(__strong nob_defer_block_t *block Ref)
{
nob_defer_block_t actual Block = *block Ref;
actual Block();
}
// the core macro
#define nob_defer(defer Block) \
__strong nob_defer_block_t nob_macro_concat(__nob_stack_defer_block_, __LINE__) __attribute__((cleanup(nob_defer Func), unused)) = defer Block
Blocks used with nob_defer
are executed in reverse order, just like Swift defer
statements:
#include <nob_defer.h>
- (void)deal With File
{
FILE *file = fopen(…);
nob_defer(^{
if (file) {
fclose(file);
}
});
// continue code where any scope exit will
// lead to the defer being executed
}
- (void)deal With Error
{
__block NSError *scope Error = nil;
nob_defer(^{
if (scope Error) {
[self perform Custom Error Handling: scope Error];
}
});
// assign any errors to "scope Error" to handle the error
// on exit, no matter how you exit
}
#define NOBDefer Release(cf Type Ref) nob_defer(^{ if (cf Type Ref) { CFRelease(cf Type Ref); } })
- (void)clean Up CFType Ref
{
CFString Ref string Ref = ... some code to create a CFString Ref ...;
NOBDefer Release(string Ref);
// continue working without having to worry
// about the CFType Ref needing to be released
}
I’ve been using my custom defer macro in production code since June and it is really the Bee’s Knees!
Swift Protocol Extensions
From Juraj Hilje:
Keep inheritance trees shallow and use protocol composition:
protocol Hello {
func say Hello() -> String
}
extension Hello {
func say Hello() -> String {
return "Hello, stranger"
}
}
class My Class: Hello {
}
let c = My Class()
c.say Hello() // "Hello, stranger"
Public Read-only Variables
From Stephen Celis:
Classes commonly have public, read-only properties but need the ability privately modify them. I’ve come across the following pattern a few times:
public class Person {
public var name: String {
return _name
}
private var _name: String
…
}
Luckily, there’s a better, oft-overlooked way that avoids the extra variable:
public class Person {
public private(set) var name: String
…
}
where
Everywhere
Swift From Taylor Franklin:
The addition of the
where
clause has made my code simple and compact while remaining readable. In addition, it has a broad application in Swift, so that it can be applied in nearly any kind of control-flow statement, such asfor
loop,while
loop,if
,guard
,switch
, and even in extension declarations. One simple way I like to use it is in myprepare
method:For Segue
if let segue ID = segue.identifier where segue ID == "my Segue" {
...
}
The combo of unwrapping and performing a condition check is most commonly where I use the
where
clause. Thewhere
clause is not going to change your life, but it should be an easy and useful addition to your Swift skills.
Improved Optional Binding
From Ursu Dan:
The improved optional binding in Swift is amazing and I use it virtually everywhere now and avoid the pyramid of doom:
if let
url = NSBundle.main Bundle().URLFor Resource("users", with Extension: "json"),
data = NSData(contents Of URL: url),
deserialized = try? NSJSONSerialization.JSONObject With Data(data, options: []),
user List = deserialized as? [ [String: Any Object] ]
{
for user Dict in user List {
if let
id = user Dict["id"] as? Int,
name = user Dict["name"] as? String,
username = user Dict["username"] as? String,
email = user Dict["email"] as? String,
phone = user Dict["phone"] as? String,
address = user Dict["address"] as? [String: Any Object]
{
users.append(User(id: id, name: name, ...))
}
}
}
xcodebuild
Output
Unbuffered From Cédric Luthi:
Using
xcpretty
because the output ofxcodebuild test
is unreadable? Unfortunately, the output of the test results becomes buffered when piped. Solution: set theNSUnbuffered
environment variable for a smooth experience. 😎IO
env NSUnbuffered IO=YES xcodebuild [flags] | xcpretty
Multiline Labels in a Table View
From Ray Fix:
Using autolayout to toggle a label in a table view from one line to many:
table View.begin Updates()
label.number Of Lines = label.number Of Lines == 0 ? 1 : 0
table View.end Updates()
You can see Ray’s technique in action in an example project:
Am IRunning As An Extension
Another from Nolan O’Brien:
With extensions in iOS, it is critical that frameworks that can be linked to both extensions and apps be cognizant of their uses so they don’t call any APIs that might not be available to an extension (like UIApplication). Here’s a function to help determine if you are running in an extension at runtime:
(Per Apple, an extension will have a top level “NSExtension” dictionary in the info.plist.)
let NOBAm IRunning As An Extension: Bool = {
let extension Dictionary: Any Object? = NSBundle.main Bundle().info Dictionary?["NSExtension"]
return extension Dictionary?.is Kind Of Class(NSDictionary.self) ?? false
}()
FOUNDATION_EXTERN BOOL Am IRunning As An Extension() __attribute__((const));
BOOL NOBAm IRunning As An Extension()
{
static BOOL s Is Extension;
static dispatch_once_t s Once Token;
dispatch_once(& s Once Token, ^{
NSDictionary *extension Dictionary = [[NSBundle main Bundle] info Dictionary][@"NSExtension"];
s Is Extension = [extension Dictionary is Kind Of Class:[NSDictionary class]];
});
return s Is Extension;
}
That frees you to do things like this:
- (void)start Background Task
{
#if TARGET_OS_IPHONE
if (!NOBAm IRunning As An Extension()) {
Class UIApplication Class = NSClass From String(@"UIApplication");
id shared Application = [UIApplication Class shared Application];
self.background Task Identifier = [shared Application begin Background Task With Expiration Handler:^{
if (self.background Task Identifier != UIBackground Task Invalid) {
[shared Application end Background Task:self.background Task Identifier];
self.background Task Identifier = UIBackground Task Invalid;
}
}];
}
#endif
}
- (void)end Background Task
{
#if TARGET_OS_IPHONE
if (self.background Task Identifier != UIBackground Task Invalid) {
NSAssert(!NOBAm IRunning As An Extension());
Class UIApplication Class = NSClass From String(@"UIApplication");
id shared Application = [UIApplication Class shared Application];
[shared Application end Background Task:self.background Task Identifier];
self.background Task Identifier = UIBackground Task Invalid;
}
#endif
}
Beyond Breakpoints
From Matthew Flint:
This has been my year of truly appreciating Xcode breakpoints, beyond the standard “break at this line” type. It breaks my heart to see other developers not using them to their potential.
Right click on a breakpoint and choose Edit Breakpoint… for access to advanced features:
Particularly the ones with actions (such as logging to the console) that continue automatically without pausing any threads, because you can add/edit them without recompiling. I’ll never accidentally commit NSLogs again. :)
po frame
Printing
Fix Console GitHub user @biggercoffee reminds us that po frame
printing fails in the LLDB console by default:
Fix it mid-debugging session with expr @import UIKit
, or fix it once and for all by adding a couple lines to your “.lldbinit”. From the command line:
touch ~/.lldbinit
echo display @import UIKit >> ~/.lldbinit
echo target stop-hook add -o \"target stop-hook disable\" >> ~/.lldbinit
-DDEBUG
in Swift
Avoiding From GitHub user @vlat456:
For those, who like me, are trying to avoid the mess with
-DDEBUG
in Swift, but have to know which version of executable is running, Debug or Release.
// Pre Processor Macros.m:
#include "Pre Processor Macros.h"
#ifdef DEBUG
BOOL const DEBUG_BUILD = YES;
#else
BOOL const DEBUG_BUILD = NO;
#endif
// Pre Processor Macros.h:
#ifndef Pre Processor Macros_h
#define Pre Processor Macros_h
#include
extern BOOL const DEBUG_BUILD;
#endif /* Pre Processor Macros_h */
// in Bridged header:
#import "Pre Processor Macros.h"
And then from Swift:
if DEBUG_BUILD {
debug Print("It's Debug build")
} else {
debug Print("It's Release build")
}
Checking For Null Blocks
From Nicolás Jakubowski:
This macro for checking block nullability before executing them:
#define BLOCK_EXEC(block, ...) if (block) { block(__VA_ARGS__); };
Old and busted:
if (completion Block)
{
completion Block(arg1, arg2);
}
New and shiny:
BLOCK_EXEC(completion Block, arg1, arg2);
Swiftier GCD
From Luo Jie:
You can use enums and protocol extensions to provide a GCD convenience API:
protocol Excutable Queue {
var queue: dispatch_queue_t { get }
}
extension Excutable Queue {
func execute(closure: () -> Void) {
dispatch_async(queue, closure)
}
}
enum Queue: Excutable Queue {
case Main
case User Interactive
case User Initiated
case Utility
case Background
var queue: dispatch_queue_t {
switch self {
case .Main:
return dispatch_get_main_queue()
case .User Interactive:
return dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
case .User Initiated:
return dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
case .Utility:
return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
case .Background:
return dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)
}
}
}
enum Serial Queue: String, Excutable Queue {
case Down Load Image = "my App.Serial Queue.Down Load Image"
case Up Load File = "my App.Serial Queue.Up Load File"
var queue: dispatch_queue_t {
return dispatch_queue_create(raw Value, DISPATCH_QUEUE_SERIAL)
}
}
Downloading something then could be written like this:
Queue.User Initiated.execute {
let url = NSURL(string: "http://image.jpg")!
let data = NSData(contents Of URL: url)!
let image = UIImage(data: data)
Queue.Main.execute {
image View.image = image
}
}
_Objective CBridgeable
From Mathew Huusko V:
Using Swift’s _ObjectiveCBridgeable (implicit castability between types) to create a generic protocol for Obj-C compatible objects that wrap pure Swift structs (keeping Swift framework API clean, but Obj-C compatible for as long as desired).
This first part defines an extension with default implementations for the bridging bookkeeping methods:
public protocol Backed Object Type: Any Object {
typealias Backing
var backing Object: Backing { get }
init(_ backing Object: Backing)
}
public protocol Object Backable: _Objective CBridgeable {
typealias Backed: Backed Object Type
}
public extension Object Backable where Backed.Backing == Self {
static func _is Bridged To Objective C() -> Bool {
return true
}
static func _get Objective CType() -> Any.Type {
return Backed.self
}
func _bridge To Objective C() -> Backed {
return Backed(self)
}
static func _force Bridge From Objective C(source: Backed, inout result: Self?) {
result = source.backing Object
}
static func _conditionally Bridge From Objective C(source: Backed, inout result: Self?) -> Bool {
_force Bridge From Objective C(source, result: &result)
return true
}
func to Bridged Object() -> Backed {
return _bridge To Objective C()
}
}
Here the Swift struct Some
and Objective-C class M5Some
are declared and bridged. Bridging between them is accomplished with an as
cast:
public struct Some Model {
public let ID: Int
public let name: String
public let category: String
}
extension Some Model: Object Backable {
public typealias Backed = M5Some Model
}
@objc public final class M5Some Model: NSObject, Backed Object Type {
public let backing Object: Some Model
public init(_ backing Object: Some Model) {
self.backing Object = backing Object
}
public var ID: Int { return backing Object.ID }
public var name: String { return backing Object.name }
public var category: String { return backing Object.category }
}
// Usage:
let model = Some Model(ID: 2, name: "awesome", category: "music")
let objc Compatible Model = model as M5Some Model
let original Model = objc Compatible Model as Some Model
Phantom Types
Josip Ćavar writes in about getting additional type safety with phantom types. In the example below, Kilometer
and Meter
are used to constrain what kinds of Distance
instances can be added together:
struct Kilometer {}
struct Meter {}
struct Distance T<T> {
private let value: Int
init(value: Int) {
self.value = value
}
}
func +<T>(left: Distance T<T>, right: Distance T<T>) -> Distance T<T> {
return Distance T(value: left.value + right.value)
}
extension Int {
var km: Distance T<Kilometer> {
return Distance T<Kilometer>(value: self)
}
var m: Distance T<Meter> {
return Distance T<Meter>(value: self)
}
}
let distance Kilometers = 5.km
let distance Meters = 15.m
let new Distance = distance Kilometers + distance Kilometers // Ok
let new Distance = distance Kilometers + distance Meters // Compiler error
Easier Configuration
From Kyle Van Essen by way of Orta Therox comes a function that streamlines multi-step initialization and configuration processes.
@warn_unused_result
public func Init<Type>(value : Type, @noescape block: (object: Type) -> Void) -> Type
{
block(object: value)
return value
}
func example()
{
let label = Init(UILabel()) {
$0.font = UIFont.bold System Font Of Size(13.0)
$0.text = "Hello, World"
$0.text Alignment = .Center
}
}
Well, that rounds out this year’s list—thanks again to all who contributed!
Happy New Year! May your code continue to compile and inspire.