Create a gist now

Instantly share code, notes, and snippets.

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

Greetings and salutations, NSHipsters!

As the year winds down, it's a tradition here at NSHipster to ask you, dear readers, to offer up your favorite tricks and tips from the past year as gifts to your fellow hipsters. With iOS 9, El Capitan, brand new watch- and tvOS's, and the open-sourcing of some minor Apple-related tech, there's bound to be lots to share.

Submit your favorite piece of Swift or @objc trivia, helpful hints, unexpected discoveries, useful workarounds, useless fascinations, or anything else you found cool this year. Just comment below!

If you need inspiration, try the list from last year, or from the year before, or from the year before that.

새해 복 많이 받으세요! 🎆

@mflint
mflint commented Dec 23, 2015

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.

@jurajhilje

Swift: Protocol Extensions

Keep inheritance trees shallow and use protocol composition:

protocol Hello {
    func sayHello() -> String
}

extension Hello {
    func sayHello() -> String {
        return "Hello, stranger"
    }
}

class MyClass: Hello {
}

let c = MyClass()
c.sayHello() // "Hello, stranger"
@stephencelis

Swift: public private(set)

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
    //
}
@0xced
0xced commented Dec 23, 2015

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
@NSProgrammer

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 :) http://openradar.appspot.com/radar?id=6105823419826176

// some helper declarations

#define _nob_macro_concat(a, b) a##b
#define nob_macro_concat(a, b) _custom_macro_concat(a, b)
typedef void(^nob_defer_block_t)();
NS_INLINE void nob_deferFunc(__strong nob_defer_block_t *blockRef)
{
   nob_defer_block_t actualBlock = *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

// use via macro in code

#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
{
    __block NSError *scopeError = nil;
    nob_defer(^{
        if (scopeError) {
             [self performCustomErrorHandling: scopeError];
        }
    });

    // do work and anytime an error is encounter assign it to "stackError" to handle the error on exit, no matter how you exit
}

#define NOBDeferRelease(cfTypeRef) nob_defer(^{ if (cfTypeRef) { CFRelease(cfTypeRef); } })

- (void)cleanUpCFTypeRef
{
     CFStringRef stringRef = ... some code to create a CFStringRef ...;
     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!

@NSProgrammer

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:

FOUNDATION_EXTERN BOOL NOBAmIRunningAsAnExtension() __attribute__((const));

BOOL NOBAmIRunningAsAnExtension()
{
    // Per Apple, an extension will have a top level NSExtension dictionary in the info.plist
    // https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/SystemExtensionKeys.html

    static BOOL sIsExtension;
    static dispatch_once_t sOnceToken;
    dispatch_once(& sOnceToken, ^{
        NSDictionary *extensionDictionary = [[NSBundle mainBundle] infoDictionary][@"NSExtension"];
        sIsExtension = [extensionDictionary isKindOfClass:[NSDictionary class]];
    });
    return sIsExtension;
}

That frees you to do things like this:

- (void)startBackgroundTask
{
#if TARGET_OS_IPHONE
    if (!NOBAmIRunningAsAnExtension()) {
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        id sharedApplication = [UIApplicationClass sharedApplication];
        self.backgroundTaskIdentifier = [sharedApplication beginBackgroundTaskWithExpirationHandler:^{
            if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
                [sharedApplication endBackgroundTask:self.backgroundTaskIdentifier];
                self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
            }
        }];
    }
#endif
}

- (void)endBackgroundTask
{
#if TARGET_OS_IPHONE
    if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
        NSAssert(!NOBAmIRunningAsAnExtension());
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        id sharedApplication = [UIApplicationClass sharedApplication];
        [sharedApplication endBackgroundTask:self.backgroundTaskIdentifier];
        self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    }
#endif
}
@jakunico

This macro for checking block nullability before executing them:

#define BLOCK_EXEC(block, ...) if (block) { block(__VA_ARGS__); };

Moves from this:

if (completionBlock)
{
    completionBlock(arg1, arg2);
}

to:

BLOCK_EXEC(completionBlock, arg1, arg2);

source

@rayfix
rayfix commented Dec 24, 2015

Using autolayout to toggle a label in a table view from one line to many:

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

https://github.com/rayfix/MultilineDemo

@biggercoffee

In the console support po frame printing
译: 在控制台里支持 po frame 打印

open terminal :

  1. touch ~/.lldbinit
  2. echo display @import UIKit >> ~/.lldbinit
  3. echo target stop-hook add -o "target stop-hook disable" >> ~/.lldbinit
@vlat456
vlat456 commented Dec 24, 2015

I did found it somewhere on stackowerflow.
For these, 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.

In code:

if DEBUG_BUILD {
debugPrint("It's Debug build")
} else {
debugPrint("It's Release build")
}

PreProcessorMacros.m:

include "PreProcessorMacros.h"

ifdef DEBUG

BOOL const DEBUG_BUILD = YES;

else

BOOL const DEBUG_BUILD = NO;

endif

PreProcessorMacros.h:

ifndef PreProcessorMacros_h

define PreProcessorMacros_h

include <Foundation/Foundation.h>

extern BOOL const DEBUG_BUILD;

endif /* PreProcessorMacros_h */

in Bridged header:

import "PreProcessorMacros.h"

@beeth0ven

You can use enum and protocol extension to provide GCD convenience API:

protocol ExcutableQueue {
    var queue: dispatch_queue_t { get }
}

extension ExcutableQueue {
    func execute(closure: () -> Void) {
        dispatch_async(queue, closure)
    }
}

enum Queue: ExcutableQueue {
    case Main
    case UserInteractive
    case UserInitiated
    case Utility
    case Background

    var queue: dispatch_queue_t {
        switch self {
        case .Main:
            return dispatch_get_main_queue()
        case .UserInteractive:
            return dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
        case .UserInitiated:
            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 SerialQueue: String, ExcutableQueue {
    case DownLoadImage = "myApp.SerialQueue.DownLoadImage"
    case UpLoadFile = "myApp.SerialQueue.UpLoadFile"

    var queue: dispatch_queue_t {
        return dispatch_queue_create(rawValue, DISPATCH_QUEUE_SERIAL)
    }
}

Download Something could write like this:

Queue.UserInitiated.execute {

    let url = NSURL(string: "http://image.jpg")!
    let data = NSData(contentsOfURL: url)!
    let image = UIImage(data: data)

    Queue.Main.execute {
        imageView.image = image
    }
}
@thebugcode

The improved optional binding in Swift is amazing and I use it virtually everywhere now and avoid the pyramid of doom:

if let
    path     = NSBundle.mainBundle().pathForResource("users", ofType: "json"),
    url      = NSURL(fileURLWithPath: path),
    data     = NSData(contentsOfURL: url),
    userList = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? [[String: AnyObject]] 
{
    // extract individual users
    for userDict in userList {
        if let
            id      = userDict["id"] as? Int,
            name    = userDict["name"] as? String,
            email   = userDict["email"] as? String,
            address = userDict["address"] as? [String: AnyObject]
        {
            users.append(User(id: id, name: name, ...))
        }
    }
}
@mhuusko5

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), e.g.:

public protocol BackedObjectType: AnyObject {
    typealias Backing

    var backingObject: Backing { get }

    init(_ backingObject: Backing)
}

public protocol ObjectBackable: _ObjectiveCBridgeable {
    typealias Backed: BackedObjectType
}

public extension ObjectBackable where Backed.Backing == Self {
    static func _isBridgedToObjectiveC() -> Bool { return true }

    static func _getObjectiveCType() -> Any.Type { return Backed.self }

    func _bridgeToObjectiveC() -> Backed { return Backed(self) }

    static func _forceBridgeFromObjectiveC(source: Backed, inout result: Self?) { result = source.backingObject }

    static func _conditionallyBridgeFromObjectiveC(source: Backed, inout result: Self?) -> Bool {
        _forceBridgeFromObjectiveC(source, result: &result)
        return true
    }

    func toBridgedObject() -> Backed { return _bridgeToObjectiveC() }
}

/////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////

public struct SomeModel {
    public let ID: Int
    public let name: String
    public let category: String
}

/////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////

extension SomeModel: ObjectBackable { public typealias Backed = M5SomeModel }

@objc public final class M5SomeModel: NSObject, BackedObjectType {
    public let backingObject: SomeModel
    public init(_ backingObject: SomeModel) { self.backingObject = backingObject }

    public var ID: Int { return backingObject.ID }
    public var name: String { return backingObject.name }
    public var category: String { return backingObject.category }
}

/////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////     /////

let model = SomeModel(ID: 2, name: "awesome", category: "music")

let objcCompatibleModel = model as M5SomeModel

let originalModel = objcCompatibleModel as SomeModel
@tfrank64

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:

if let segueID = segue.identifier where segueID == "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.

@jcavar
jcavar commented Dec 30, 2015

Additional type safety with phantom types:

struct Kilometer {}
struct Meter {}

struct DistanceT<T> {
    private let value: Int

    init(value: Int) {
        self.value = value
    }
}

func +<T>(left: DistanceT<T>, right: DistanceT<T>) -> DistanceT<T> {
    return DistanceT(value: left.value + right.value)
}

extension Int {
    var km: DistanceT<Kilometer> {
        return DistanceT<Kilometer>(value: self)
    }
    var m: DistanceT<Meter> {
        return DistanceT<Meter>(value: self)
    }
}


let distanceKilometers = 5.km
let distanceMeters = 15.m
let newDistance = distanceKilometers + distanceKilometers // Ok
let newDistance = distanceKilometers + distanceMeters // Compiler error
@joemasilotti

How to tell if your app is running under UI Tests:

UI Tests:

class UITests: TestCase {
    let app = XCUIApplication()

    override func setUp() {
        super.setUp()
        app.launchArguments = ["UI-TESTING"]
        app.launch()
    }
}

App Code:

func UITesting() -> Bool {
    return NSProcessInfo.processInfo().arguments.contains("UI-TESTING")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment