NSErrors as categories

Leave a comment

Instead of having manual one off error methods like this

[self.delegate connectionCoordinator:self didFailConnectionAttemptWithError:[self errorCouldNotWakeup]];
- (NSError *)errorCouldNotWakeup
    NSDictionary *userInfo = @{
            NSLocalizedDescriptionKey: @"Could not wake up.",
            NSLocalizedFailureReasonErrorKey: @"Application may not be installed.",
            NSLocalizedRecoverySuggestionErrorKey: @"Ensure application is installed."
    return [NSError errorWithDomain:RemoteErrorDomain

A nice alternative is to decorate NSError with categories.

[self.delegate connectionCoordinator:self
               didFailConnectionAttemptWithError:[NSError xxx_backgroundWakeupFailedErrorWithUnderlyingError:nil]];


@interface NSError (Remote)

 *  Constructs a user facing error for a connection terminated event.
 *  @param underlyingError The underlying error which caused the event, if any.
 *  @return A new user facing error.
+ (instancetype)xxx_connectionTerminatedErrorWithUnderlyingError:(nullable NSError *)underlyingError;


+ (instancetype)xxx_errorWithCode:(NSInteger)code userInfo:(NSDictionary *)userInfo underlyingError:(nullable NSError *)underlyingError
    NSMutableDictionary * const mutableUserInfo = [userInfo mutableCopy];

    if (underlyingError != nil) {
        mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;

    return [NSError errorWithDomain:RemoteErrorDomain
                           userInfo:[mutableUserInfo copy]];

+ (instancetype)xxx_connectionTerminatedErrorWithUnderlyingError:(nullable NSError *)underlyingError
    NSMutableDictionary * const userInfo = [NSMutableDictionary dictionaryWithDictionary:@{
        NSLocalizedDescriptionKey: @"Connection terminated.",
        NSLocalizedFailureReasonErrorKey: @"The app might have been suspended.",
        NSLocalizedRecoverySuggestionErrorKey: @"Try to reconnect."

    return [self xxx_errorWithCode:RemoteConnectionTerminatedError

And then use it like this

- (void)transport:(id<RemoteTransport>)transport didDisconnectWithError:(NSError *)error
    NSError * const userFacingError = [NSError xxx_connectionTerminatedErrorWithUnderlyingError:error];
    [self.delegate Remote:self didDisconnectWithError:userFacingError];

iOS Logger

Leave a comment

Here is an example of how to add a logger with levels to an app. So instead of going

NSLog(@"Found Match %@.", info);

You can go

LogD(@"Found Match %@.", info);
LogE(@"Could not find.");


typedef NS_ENUM(NSUInteger, RemoteLogLevel) {
  /// Do not log at all.
  RemoteLogLevelNone = 0,
  /// Log debug, info and error messages.
  RemoteLogLevelDebug = 1,
  /// Log info and error messages.
  RemoteLogLevelInfo = 2,
  /// Log only error messages.
  RemoteLogLevelError = 3,

* @param logLevel The lowest severity to log to console.

- (instancetype)initWithLogLevel:(RemoteLogLevel)logLevel NS_DESIGNATED_INITIALIZER;


- (instancetype)initWithLogLevel:(RemoteLogLevel)logLevel
  self = [super init];

  if (self) {
    [RemoteLogger sharedLogger].logLevel = logLevel;

  return self;

- (void)connect
  [self.transport connect];



#define LogD(fmt,...) [[RemoteLogger sharedLogger] logMessage:[NSString stringWithFormat:(fmt), ##__VA_ARGS__] \

#define LogI(fmt,...) [[RemoteLogger sharedLogger] logMessage:[NSString stringWithFormat:(fmt), ##__VA_ARGS__] \

#define LogE(fmt,...) [[RemoteLogger sharedLogger] logMessage:[NSString stringWithFormat:(fmt), ##__VA_ARGS__] \

/// The @c RemoteLogger is a singleton that handles logging messages to console, with different log levels.
@interface RemoteLogger : NSObject

/// Returns the shared instance of the logger.
+ (instancetype)sharedLogger;

/// The lowest log level that the logger will log to console.
@property (nonatomic, assign, readwrite) RemoteLogLevel logLevel;

* Logs the message at the given level.
* @param message The message to log.
* @param level The severity of the message.
- (void)logMessage:(NSString *)message level:(RemoteLogLevel)level;



#import "RemoteLogger.h"

@implementation RemoteLogger

+ (instancetype)sharedLogger
  static RemoteLogger *sharedLogger = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&amp;onceToken, ^{
  sharedLogger = [self new];
  sharedLogger.logLevel = RemoteLogLevelInfo;
return sharedLogger;

- (void)logMessage:(NSString *)message level:(RemoteLogLevel)level
  if (level &lt; self.logLevel) {

  NSLog(@"Remote: %@", message);


Image Representable

Leave a comment

If you have have a NSString and you want to give it a stronger type try representable.

- (void)fetchImageWithID:(NSString *)imageID
- (void)fetchImageForItem:(id)imageRepresentable withSize:(CGSize)imageSize;

Then you can setup it up and use it like this.

NSString *imageID = imageRepresentable.imageIdentifier;
NSMutableDictionary *namedArguments = [NSMutableDictionary dictionaryWithDictionary:@{
mageIdentifierKey: imageID

if (!CGSizeEqualToSize(imageSize, CGSizeZero)) {
namedArguments[ImageWidthKey] = @(imageSize.width);
namedArguments[ImageHeightKey] = @(imageSize.height);

[self.client performRPC:SPTAppRemoteGetImageCallMethodName

* The @c ImageRepresentable protocol represents an object that may contain an image.
@protocol ImageRepresentable

/// The identifier of the image of the entity.
@property (nonatomic, strong, readonly) NSString *imageIdentifier;

Autolayout Take 4

Leave a comment

Here is how you hang one button beneath another.
You have x2 constraints you need to fullfill: x and y.

It’s confusing because we are going to draw x2 verticals lines, but the first is actually an x constraint.

The first constraint we are going to add is going to center the button horizontally under the upper button.

Screen Shot 2016-04-25 at 10.39.41 AM.png

Which results in

Screen Shot 2016-04-25 at 10.39.49 AM.png

So this is an x constraint – even though it looks visually like a y constraint. It’s red because we haven’t told it yet how to hang in the y. So we are going to do that now by adding vertical spacing by control dragging from the button button to the subscribe above.

Screen Shot 2016-04-25 at 10.40.10 AM.png

Which results it

Screen Shot 2016-04-25 at 10.40.17 AM.png

Now all our constraints have been filled and we are good to go!

Autolayout Take 3

Leave a comment

Here is how you do simple autolayout. Everything hinges of one element. So start at the top and work your way down.Screen Shot 2016-04-13 at 2.55.43 PM.png

Anchor Track Name to the top by dragging from label towards the top.

Screen Shot 2016-04-13 at 2.55.57 PM.png

This will give you one constraint. But you still need something to anchor you vertically.

Screen Shot 2016-04-13 at 2.56.09 PM.png

Control drag from Track Name down

Screen Shot 2016-04-13 at 2.59.21 PM.png

Screen Shot 2016-04-13 at 2.59.28 PM.png

Blue means you are good. Orange means you are OK, it just wants to adjust your layout. But Track Name is now good.

Next let’s anchor the Play button directly underneath the Track Name. Control drag from Play button to Track name again.

Screen Shot 2016-04-13 at 3.04.21 PM.pngScreen Shot 2016-04-13 at 3.04.28 PM.png


Now, same problem as before, Play is partially defined. We need to anchor it too. Control drag again down into the open.

Screen Shot 2016-04-13 at 3.07.21 PM.pngScreen Shot 2016-04-13 at 3.07.28 PM.png

Now we need to hook up the next previous buttons. Here we can control drag from Play to each selecting Horizontal spacing.

Screen Shot 2016-04-13 at 3.11.25 PM.pngScreen Shot 2016-04-13 at 3.11.57 PM.png



And then the magic move is to a line on the play button by going

Screen Shot 2016-04-13 at 3.44.46 PM.pngScreen Shot 2016-04-13 at 3.44.52 PM.png

And repeating for the next button.

For the ImageView let’s set it up so that there is a always a margin to the right. Control drag from image view to the right border


Screen Shot 2016-04-13 at 3.46.21 PM.png


Screen Shot 2016-04-13 at 3.46.39 PM.png

Now to get it to respect the border, click on the constraint and go adjust the options on the right margin as follows

Screen Shot 2016-04-13 at 3.47.51 PM.pngScreen Shot 2016-04-13 at 3.48.35 PM.png

This means give me at least a 15 margin, with a lower priority of 500.

Repeat for the other side and bottom

Screen Shot 2016-04-13 at 3.50.27 PM.png


Now another magic move is to have a constraint on it’s aspect ratio so it stays a square.

Screen Shot 2016-04-13 at 3.51.05 PM.png

And of course anchor it to the top via the play button.

Then to fix the final layout issues on the image view add various anchors around the image view, and you can always check and see what constraints are missing by looking in the upper right hand corner.


Screen Shot 2016-04-13 at 4.12.50 PM.png

Screen Shot 2016-04-13 at 4.12.57 PM.png

Screen Shot 2016-04-13 at 4.13.04 PM.png

Screen Shot 2016-04-13 at 4.13.09 PM.png


AppCode Formatting

Leave a comment

When you go Alt + Option + Command you can instantly reformat all your code in AppCode. But sometimes you want to adjust the layout to meet your teams coding standard.

Here is a list of tweaks I have had to make and where to find them in AppCode.

Adding a space after a property

Screen Shot 2016-04-11 at 8.22.08 AM.png

Ones I haven’t figured out yet

There should be no space between id and Protocol here.

@property(nonatomic, strong) id&lt;PlayerAPI&gt; playerAPI;

Space after const in variable

ConnectionParams * const connectionParams =

AppDoc examples

Leave a comment

/// @c YES if the user can play songs on demand, otherwise @c NO.

 *  Fetches the foo.
 *  @param callback A callback block that will be invoked when the fetch request has completed.
 *                  On success `result` will be populated by an instance of `id<Foo>`.
 *                  On error `result` will be `nil` and `error` will be set.
- (void)fetchFooWithCallack:(nullable FooCallback)callback;

 * Initializes a new user API.
 * @discussion This is the designated initializer.
 * @param client A client responsible for sending and receiving messages
- (instancetype)initWithClient:(Client *)client;

Older Entries


Get every new post delivered to your Inbox.

Join 348 other followers