Quantcast
Channel: NSHipster
Viewing all articles
Browse latest Browse all 382

Reader Submissions - New Year's 2016

$
0
0

With 2015 behind us and the new year begun, it’s time again for an NSHipster tradition: reader submissions! As inyear’spast, 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!


Swift’s defer in Objective-C

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 a defer 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)typedefvoid(^nob_defer_block_t)();NS_INLINEvoidnob_deferFunc(__strongnob_defer_block_t*blockRef){nob_defer_block_tactualBlock=*blockRef;actualBlock();}// the core macro#define nob_defer(deferBlock) \__strong nob_defer_block_t nob_macro_concat(__nob_stack_defer_block_, __LINE__) __attribute__((cleanup(nob_deferFunc), unused)) = deferBlock

Blocks used with nob_defer are executed in reverse order, just like Swift defer statements:

#include <nob_defer.h>-(void)dealWithFile{FILE*file=fopen();nob_defer(^{if(file){fclose(file);}});// continue code where any scope exit will // lead to the defer being executed}-(void)dealWithError{__blockNSError*scopeError=nil;nob_defer(^{if(scopeError){[selfperformCustomErrorHandling:scopeError];}});// assign any errors to "scopeError" to handle the error// on exit, no matter how you exit}#define NOBDeferRelease(cfTypeRef) nob_defer(^{ if (cfTypeRef) { CFRelease(cfTypeRef); } })-(void)cleanUpCFTypeRef{CFStringRefstringRef=...somecodetocreateaCFStringRef...;NOBDeferRelease(stringRef);// continue working without having to worry// about the CFTypeRef 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:

protocolHello{funcsayHello()->String}extensionHello{funcsayHello()->String{return"Hello, stranger"}}classMyClass:Hello{}letc=MyClass()c.sayHello()// "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:

publicclassPerson{publicvarname:String{return_name}privatevar_name:String// ...}

Luckily, there’s a better, oft-overlooked way that avoids the extra variable:

publicclassPerson{publicprivate(set)varname:String// ...}

Swift where Everywhere

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 as for loop, while loop, if, guard, switch, and even in extension declarations. One simple way I like to use it is in my prepareForSegue method:

ifletsegueID=segue.identifierwheresegueID=="mySegue"{...}

The combo of unwrapping and performing a condition check is most commonly where I use the where clause. The where 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:

ifleturl=NSBundle.mainBundle().URLForResource("users",withExtension:"json"),data=NSData(contentsOfURL:url),deserialized=try?NSJSONSerialization.JSONObjectWithData(data,options:[]),userList=deserializedas?[[String:AnyObject]]{foruserDictinuserList{ifletid=userDict["id"]as?Int,name=userDict["name"]as?String,username=userDict["username"]as?String,email=userDict["email"]as?String,phone=userDict["phone"]as?String,address=userDict["address"]as?[String:AnyObject]{users.append(User(id:id,name:name,...))}}}

Unbuffered xcodebuild Output

From Cédric Luthi:

Using xcpretty because the output of xcodebuild test is unreadable? Unfortunately, the output of the test results becomes buffered when piped. Solution: set the NSUnbufferedIO environment variable for a smooth experience. 😎

env NSUnbufferedIO=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:

tableView.beginUpdates()label.numberOfLines=label.numberOfLines==0?1:0tableView.endUpdates()

You can see Ray’s technique in action in an example project:

Multiline demo


AmIRunningAsAnExtension

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.)

letNOBAmIRunningAsAnExtension:Bool={letextensionDictionary:AnyObject?=NSBundle.mainBundle().infoDictionary?["NSExtension"]returnextensionDictionary?.isKindOfClass(NSDictionary.self)??false}()
FOUNDATION_EXTERNBOOLAmIRunningAsAnExtension()__attribute__((const));BOOLNOBAmIRunningAsAnExtension(){staticBOOLsIsExtension;staticdispatch_once_tsOnceToken;dispatch_once(&sOnceToken,^{NSDictionary*extensionDictionary=[[NSBundlemainBundle]infoDictionary][@"NSExtension"];sIsExtension=[extensionDictionaryisKindOfClass:[NSDictionaryclass]];});returnsIsExtension;}

That frees you to do things like this:

-(void)startBackgroundTask{#if TARGET_OS_IPHONEif(!NOBAmIRunningAsAnExtension()){ClassUIApplicationClass=NSClassFromString(@"UIApplication");idsharedApplication=[UIApplicationClasssharedApplication];self.backgroundTaskIdentifier=[sharedApplicationbeginBackgroundTaskWithExpirationHandler:^{if(self.backgroundTaskIdentifier!=UIBackgroundTaskInvalid){[sharedApplicationendBackgroundTask:self.backgroundTaskIdentifier];self.backgroundTaskIdentifier=UIBackgroundTaskInvalid;}}];}#endif}-(void)endBackgroundTask{#if TARGET_OS_IPHONEif(self.backgroundTaskIdentifier!=UIBackgroundTaskInvalid){NSAssert(!NOBAmIRunningAsAnExtension());ClassUIApplicationClass=NSClassFromString(@"UIApplication");idsharedApplication=[UIApplicationClasssharedApplication];[sharedApplicationendBackgroundTask:self.backgroundTaskIdentifier];self.backgroundTaskIdentifier=UIBackgroundTaskInvalid;}#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:

Breakpoint Options Popup

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. :)


Fix Console po frame Printing

GitHub user @biggercoffee reminds us that po frame printing fails in the LLDB console by default:

Broken po frame

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

Fixed po frame


Avoiding -DDEBUG in Swift

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.

// PreProcessorMacros.m:#include "PreProcessorMacros.h"#ifdef DEBUGBOOLconstDEBUG_BUILD=YES;#elseBOOLconstDEBUG_BUILD=NO;#endif// PreProcessorMacros.h:#ifndef PreProcessorMacros_h#define PreProcessorMacros_h#includeexternBOOLconstDEBUG_BUILD;#endif /* PreProcessorMacros_h */// in Bridged header:#import "PreProcessorMacros.h"

And then from Swift:

ifDEBUG_BUILD{debugPrint("It's Debug build")}else{debugPrint("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(completionBlock){completionBlock(arg1,arg2);}

New and shiny:

BLOCK_EXEC(completionBlock,arg1,arg2);

Swiftier GCD

From Luo Jie:

You can use enums and protocol extensions to provide a GCD convenience API:

protocolExcutableQueue{varqueue:dispatch_queue_t{get}}extensionExcutableQueue{funcexecute(closure:()->Void){dispatch_async(queue,closure)}}enumQueue:ExcutableQueue{caseMaincaseUserInteractivecaseUserInitiatedcaseUtilitycaseBackgroundvarqueue:dispatch_queue_t{switchself{case.Main:returndispatch_get_main_queue()case.UserInteractive:returndispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE,0)case.UserInitiated:returndispatch_get_global_queue(QOS_CLASS_USER_INITIATED,0)case.Utility:returndispatch_get_global_queue(QOS_CLASS_UTILITY,0)case.Background:returndispatch_get_global_queue(QOS_CLASS_BACKGROUND,0)}}}enumSerialQueue:String,ExcutableQueue{caseDownLoadImage="myApp.SerialQueue.DownLoadImage"caseUpLoadFile="myApp.SerialQueue.UpLoadFile"varqueue:dispatch_queue_t{returndispatch_queue_create(rawValue,DISPATCH_QUEUE_SERIAL)}}

Downloading something then could be written like this:

Queue.UserInitiated.execute{leturl=NSURL(string:"http://image.jpg")!letdata=NSData(contentsOfURL:url)!letimage=UIImage(data:data)Queue.Main.execute{imageView.image=image}}

_ObjectiveCBridgeable

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:

publicprotocolBackedObjectType:AnyObject{typealiasBackingvarbackingObject:Backing{get}init(_backingObject:Backing)}publicprotocolObjectBackable:_ObjectiveCBridgeable{typealiasBacked:BackedObjectType}publicextensionObjectBackablewhereBacked.Backing==Self{staticfunc_isBridgedToObjectiveC()->Bool{returntrue}staticfunc_getObjectiveCType()->Any.Type{returnBacked.self}func_bridgeToObjectiveC()->Backed{returnBacked(self)}staticfunc_forceBridgeFromObjectiveC(source:Backed,inoutresult:Self?){result=source.backingObject}staticfunc_conditionallyBridgeFromObjectiveC(source:Backed,inoutresult:Self?)->Bool{_forceBridgeFromObjectiveC(source,result:&result)returntrue}functoBridgedObject()->Backed{return_bridgeToObjectiveC()}}

Here the Swift struct SomeModel and Objective-C class M5SomeModel are declared and bridged. Bridging between them is accomplished with an as cast:

publicstructSomeModel{publicletID:Intpublicletname:Stringpublicletcategory:String}extensionSomeModel:ObjectBackable{publictypealiasBacked=M5SomeModel}@objcpublicfinalclassM5SomeModel:NSObject,BackedObjectType{publicletbackingObject:SomeModelpublicinit(_backingObject:SomeModel){self.backingObject=backingObject}publicvarID:Int{returnbackingObject.ID}publicvarname:String{returnbackingObject.name}publicvarcategory:String{returnbackingObject.category}}// Usage:letmodel=SomeModel(ID:2,name:"awesome",category:"music")letobjcCompatibleModel=modelasM5SomeModelletoriginalModel=objcCompatibleModelasSomeModel

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 DistanceT instances can be added together:

structKilometer{}structMeter{}structDistanceT<T>{privateletvalue:Intinit(value:Int){self.value=value}}func+<T>(left:DistanceT<T>,right:DistanceT<T>)->DistanceT<T>{returnDistanceT(value:left.value+right.value)}extensionInt{varkm:DistanceT<Kilometer>{returnDistanceT<Kilometer>(value:self)}varm:DistanceT<Meter>{returnDistanceT<Meter>(value:self)}}letdistanceKilometers=5.kmletdistanceMeters=15.mletnewDistance=distanceKilometers+distanceKilometers// OkletnewDistance=distanceKilometers+distanceMeters// 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_resultpublicfuncInit<Type>(value:Type,@noescapeblock:(object:Type)->Void)->Type{block(object:value)returnvalue}funcexample(){letlabel=Init(UILabel()){$0.font=UIFont.boldSystemFontOfSize(13.0)$0.text="Hello, World"$0.textAlignment=.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.


Viewing all articles
Browse latest Browse all 382

Trending Articles