Here are my notes for working with Push Notification, updated for iOS 9

How to register

  • Register to receive push notification

registerForRemoteNotificationTypes is deprecated in iOS 8+

1
UIApplication.sharedApplication().registerForRemoteNotifications()
  • Register to alert user through UI

If your app displays alerts, play sounds, or badges its icon, you must call this method during your launch cycle to request permission to alert the user in these ways

1
2
3
4
5
let types: UIUserNotificationType = [.Badge, .Sound, .Alert]
let categories = Set<UIUserNotificationCategory>()
let settings = UIUserNotificationSettings(forTypes: types, categories: categories)

UIApplication.sharedApplication().registerUserNotificationSettings(settings)

You don’t need to wait for registerUserNotificationSettings to callback before calling registerForRemoteNotifications

When to register

Never cache a device token; always get the token from the system whenever you need it. If your app previously registered for remote notifications, calling the registerForRemoteNotifications method again does not incur any additional overhead, and iOS returns the existing device token to your app delegate immediately. In addition, iOS calls your delegate method any time the device token changes, not just in response to your app registering or re-registering

The user can change the notification settings for your app at any time using the Settings app. Because settings can change, always call the registerUserNotificationSettings: at launch time and use the application:didRegisterUserNotificationSettings: method to get the response. If the user disallows specific notification types, avoid using those types when configuring local and remote notifications for your app.

didReceiveRemoteNotification

About application:didReceiveRemoteNotification:

Implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible. If your delegate implements both methods, the app object calls the application:didReceiveRemoteNotification:fetchCompletionHandler: method.

If the app is not running when a remote notification arrives, the method launches the app and provides the appropriate information in the launch options dictionary. The app does not call this method to handle that remote notification. Instead, your implementation of the application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method needs to get the remote notification payload data and respond appropriately.

About application:didReceiveRemoteNotification:fetchCompletionHandler:

This is for silent push notification with content-available

Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running in the foreground, the system calls this method when your app is running in the foreground or background

In addition, if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a push notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.

If the user opens your app from the system-displayed alert, the system may call this method again when your app is about to enter the foreground so that you can update your user interface and display information pertaining to the notification.

How to handle

Usually, the use of push notification is to display a specific article, a specific DetailViewController, … in your app. So the good practices are

  • When the app is in foreground: Gently display some kind of alert view and ask the user whether he would like to go to that specific page or not
  • When user is brought from background to foreground, or from terminated to foreground: Just navigate to that specific page. For example, if you use UINavigationController, you can set that specific page the top most ViewController, if you use UITabBarController, you can set that specific page the selected tab, something like that
1
2
3
4
5
6
7
8
9
10
11
- func handlePushNotification(userInfo: NSDictionary) {
// Check applicationState
if (applicationState == UIApplicationStateActive) {
// Application is running in foreground
showAlertForPushNotification(userInfo)
}
else if (applicationState == UIApplicationStateBackground || applicationState == UIApplicationStateInactive) {
// Application is brought from background or launched after terminated
handlePushNotification(userInfo)
}
}

Here we create another method `handlePushNotification:`` to handle push notification. When you receive push notification, 3 cases can occur

Case 1: Foreground

Loud push

  • No system alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called

Silent push

  • No system alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called

Case 2: Background

Loud push

  • System alert
  • No method called
  • Tap notification and application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and nothing is called

Silent push

  • System alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called. If app is suspended, its state changed to UIApplicationStateBackground
  • Tap notification and application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and nothing is called

Case 3: Terminated

Loud push

  • System alert
  • No method called
  • Tap notification and application:didFinishLaunchingWithOptions: with launchOptions, application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and application:didFinishLaunchingWithOptions: is called with launchOptions set to nil

Silent push

  • System alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called. If app was not killed by user, it is woke up and state changed to UIApplicationStateInactive.
  • Tap notification and application:didFinishLaunchingWithOptions: with launchOptions, application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and application:didFinishLaunchingWithOptions: is called with launchOptions set to nil

System alert

System alert only show if the payload contains “alert”

1
2
3
4
5
6
7
8
9
10
11
12
{
"aps" : {
"alert" : {
"title" : "Game Request",
"body" : "Bob wants to play poker",
"action-loc-key" : "PLAY"
},
"badge" : 5
},
"param1" : "bar",
"param2" : [ "bang", "whiz" ]
}

Silent push payload

For now I see that silent push must contain “sound” for application:didReceiveRemoteNotification:fetchCompletionHandler: to be called when app is in background

1
2
3
4
5
6
7
8
9
{
"aps": {
"content-available": 1,
"alert": "hello" // include this if we want to show alert
"sound": "" // this does the trick
},
"param1": 1,
"param2": "text"
}

Reference

Here are my notes for working with Push Notification, updated for iOS 9

How to register

  • Register to receive push notification

registerForRemoteNotificationTypes is deprecated in iOS 8+

1
UIApplication.sharedApplication().registerForRemoteNotifications()
  • Register to alert user through UI

If your app displays alerts, play sounds, or badges its icon, you must call this method during your launch cycle to request permission to alert the user in these ways

1
2
3
4
5
let types: UIUserNotificationType = [.Badge, .Sound, .Alert]
let categories = Set<UIUserNotificationCategory>()
let settings = UIUserNotificationSettings(forTypes: types, categories: categories)

UIApplication.sharedApplication().registerUserNotificationSettings(settings)

You don’t need to wait for registerUserNotificationSettings to callback before calling registerForRemoteNotifications

When to register

Never cache a device token; always get the token from the system whenever you need it. If your app previously registered for remote notifications, calling the registerForRemoteNotifications method again does not incur any additional overhead, and iOS returns the existing device token to your app delegate immediately. In addition, iOS calls your delegate method any time the device token changes, not just in response to your app registering or re-registering

The user can change the notification settings for your app at any time using the Settings app. Because settings can change, always call the registerUserNotificationSettings: at launch time and use the application:didRegisterUserNotificationSettings: method to get the response. If the user disallows specific notification types, avoid using those types when configuring local and remote notifications for your app.

didReceiveRemoteNotification

About application:didReceiveRemoteNotification:

Implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible. If your delegate implements both methods, the app object calls the application:didReceiveRemoteNotification:fetchCompletionHandler: method.

If the app is not running when a remote notification arrives, the method launches the app and provides the appropriate information in the launch options dictionary. The app does not call this method to handle that remote notification. Instead, your implementation of the application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method needs to get the remote notification payload data and respond appropriately.

About application:didReceiveRemoteNotification:fetchCompletionHandler:

This is for silent push notification with content-available

Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running in the foreground, the system calls this method when your app is running in the foreground or background

In addition, if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a push notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.

If the user opens your app from the system-displayed alert, the system may call this method again when your app is about to enter the foreground so that you can update your user interface and display information pertaining to the notification.

How to handle

Usually, the use of push notification is to display a specific article, a specific DetailViewController, … in your app. So the good practices are

  • When the app is in foreground: Gently display some kind of alert view and ask the user whether he would like to go to that specific page or not
  • When user is brought from background to foreground, or from terminated to foreground: Just navigate to that specific page. For example, if you use UINavigationController, you can set that specific page the top most ViewController, if you use UITabBarController, you can set that specific page the selected tab, something like that
1
2
3
4
5
6
7
8
9
10
11
- func handlePushNotification(userInfo: NSDictionary) {
// Check applicationState
if (applicationState == UIApplicationStateActive) {
// Application is running in foreground
showAlertForPushNotification(userInfo)
}
else if (applicationState == UIApplicationStateBackground || applicationState == UIApplicationStateInactive) {
// Application is brought from background or launched after terminated
handlePushNotification(userInfo)
}
}

Here we create another method `handlePushNotification:`` to handle push notification. When you receive push notification, 3 cases can occur

Case 1: Foreground

Loud push

  • No system alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called

Silent push

  • No system alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called

Case 2: Background

Loud push

  • System alert
  • No method called
  • Tap notification and application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and nothing is called

Silent push

  • System alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called. If app is suspended, its state changed to UIApplicationStateBackground
  • Tap notification and application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and nothing is called

Case 3: Terminated

Loud push

  • System alert
  • No method called
  • Tap notification and application:didFinishLaunchingWithOptions: with launchOptions, application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and application:didFinishLaunchingWithOptions: is called with launchOptions set to nil

Silent push

  • System alert
  • application:didReceiveRemoteNotification:fetchCompletionHandler: called. If app was not killed by user, it is woke up and state changed to UIApplicationStateInactive.
  • Tap notification and application:didFinishLaunchingWithOptions: with launchOptions, application:didReceiveRemoteNotification:fetchCompletionHandler: called
  • Tap on App Icon and application:didFinishLaunchingWithOptions: is called with launchOptions set to nil

System alert

System alert only show if the payload contains “alert”

1
2
3
4
5
6
7
8
9
10
11
12
{
"aps" : {
"alert" : {
"title" : "Game Request",
"body" : "Bob wants to play poker",
"action-loc-key" : "PLAY"
},
"badge" : 5
},
"param1" : "bar",
"param2" : [ "bang", "whiz" ]
}

Silent push payload

For now I see that silent push must contain “sound” for application:didReceiveRemoteNotification:fetchCompletionHandler: to be called when app is in background

1
2
3
4
5
6
7
8
9
{
"aps": {
"content-available": 1,
"alert": "hello" // include this if we want to show alert
"sound": "" // this does the trick
},
"param1": 1,
"param2": "text"
}

Reference

I see that my answer to the question What’s the meaning of Base SDK, iOS deployment target, Target, and Project in xcode gets lots of views, so I think I need to elaborate more about it

Good read

Base SDK

  • We can’t configure this anymore, as Xcode will use the latest SDK. For Xcode 7, the SDK is iOS 9
  • If we upgrade Xcode, it will use the newer version of the SDK. Like Xcode 7.2, the SDK is iOS 9.1
  • Choosing the latest SDK for your project lets you use the new APIs introduced in the OS update that corresponds to that SDK. When new functionality is added as part of a system update, the system update itself does not typically contain updated header files reflecting the change. The SDKs, however, do contain updated header files.

Deployment Target

  • We can set in Xcode -> Target -> Deployment Info -> Deployment Target
  • State that we support this iOS version

What does it mean

So, a modern App might use iOS 9 as the Target SDK, and iOS 7 as the deployment target. This means that you can run on iOS 7, iOS 8 and iOS 9, and that you have available to you any iOS 9 calls when actually running on iOS 9.

.

Each .sdk directory resembles the directory hierarchy of the operating system release it represents: It has usr, System, and Developer directories at its top level. OS X .sdk directories also contain a Library directory. Each of these directories in turn contains subdirectories with the headers and libraries that are present in the corresponding version of the operating system with Xcode installed.

.

The libraries in an iOS or OS X SDK are stubs for linking only; they do not contain executable code but just the exported symbols. SDK support works only with native build targets.

So the SDK is just like stub and header only. It means that we can use certain APIs, but on OS that does not have the real symbols for those APIs, it crashes

available

Swift 2 introduces available construct that guards against failure when trying to use newer APIs.

Note that available is runtime, not compile time. All the code is inside your executable

1
2
3
4
5
if #available(iOS 9, OSX 10.10, *) {
// Code to execute on iOS 9, OS X 10.10
} else {

}

deprecated APIs

Always check to see if you are using deprecated APIs; though still available, deprecated APIs are not guaranteed to be available in the future

Compile time vs Runtime

1
2
3
4
5
#if (arch(i386) || arch(x86_64)) && os(iOS)
// code inside gets inserted into executable when builds for simulator
#else
// code inside gets inserted into executable when builds for device
#endif
1
2
3
4
5
#if os(OSX)
import Cocoa
#elseif os(iOS)
import UIKit
#endif
1
2
3
4
5
6
// All the code gets inserted into executable, but is run depending on the version of the OS
if #available(iOS 9, *) {
// use UIStackView
} else {
// show your manual Auto Layout skill
}

Weakly vs strongly linked

For example, suppose in Xcode you set the deployment target (minimum required version) to “OS X v10.5” and the base SDK (maximum allowed version) to “OS X v10.6”. During compilation, the compiler would weakly link interfaces that were introduced in OS X v10.6 while strongly linking interfaces defined in earlier versions of the OS. This would allow your application to run in OS X v10.5 and take advantage of newer features when available.

.

None of the (platform) frameworks is really “included in the bundle”. Instead, your app has a reference (“link”) to a framework once you add it to the “Link Binary with Library” build phase. The frameworks are pre-installed on the devices. When you run an app, all the app’s framework references are resolved by the dynamic linker (on the device), which means the framework code is loaded so your app can use it.

Reference

In an iOS project, we often see this in AppDelegate

1
2
3
4
5
6
7
8
9
10
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

return true
}
}

But in a Cocoa project, we see this instead

1
2
3
4
5
6
7
8
9
10
11
12
13
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {



func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}

func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}

In this case the param is of type NSNotification

Delegate and notification

Reading Cocoa Core Competencies - Delegation

The delegate of most Cocoa framework classes is automatically registered as an observer of notifications posted by the delegating object. The delegate need only implement a notification method declared by the framework class to receive a particular notification message. Following the example above, a window object posts an NSWindowWillCloseNotification to observers but sends a windowShouldClose: message to its delegate.

So the pattern is that the delegate should strip the NS and Notification, like NSWindowWillCloseNotification to windowShouldClose:

When working on Scale I think it’s good to have a way to group the digit so that it is easier to reason

Luckily, Swift already supports this. See The Swift Programming Language - Numeric Literals

Numeric literals can contain extra formatting to make them easier to read. Both integers and floats can be padded with extra zeros and can contain underscores to help with readability. Neither type of formatting affects the underlying value of the literal

1
2
3
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

Talking about grouping digits after the decimal point, it is interesting too Convention of digit grouping after decimal point

So now we have

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public enum MetricUnit: Double {
case nano = 0.000_000_001
case micro = 0.000_001
case milli = 0.001
case centi = 0.01
case deci = 0.1
case base = 1
case deka = 10
case hecto = 100
case kilo = 1_000
case mega = 1_000_000
case giga = 1_000_000_000
case tera = 1_000_000_000_000
case peta = 1_000_000_000_000_000

static var defaultScale: Double {
return MetricUnit.base.rawValue
}
}

Here are practices I found useful when developing iOS.
They can be tools or framework that greatly affect your style. Sometimes, it applies to other platform as well

Architecture

We’re always looking for ways to structure our apps better, I collect some cool stuffs in ios-architecture

Name it right

Naming is hard. But we can start by naming correctly and with correct spelling. The best way is to stick with the platform naming convention

Project structure

Some prefer to group their projects by object role, like Model, View, View Controller. But I prefer to group by feature or use case, learning from Clean Architecture

  • Clean Architecture When you look at a software system, and all you see is MCV in a web configuration, then the architecture of that system is hiding the use cases of that system and exposing the delivery mechanism.

I often group my code like this

  • Share: Contains code that can be useful to other project. Not related to any project. Can be extract to a Pod
  • Engine: Contains code that is specific to this project. Like UserManager, ViewAnimator, …
  • Modules: Separate each screen into module. In each module, there are view, view model and wireframe

There are some architectures like MVC, MVVM, VIPER, VIP, … and you may want to leverage some templates to help creating projects faster, like

And learn how to make them

There are also some scripts to bootstrap your project

  • crafter Crafter - Xcode project configuration CLI made easy
  • xcake Create your Xcode projects automatically using a stupid simple DSL.
  • liftoff CLI for creating and configuring new Xcode projects

Functional Reactive Programming

The Reactive Extension inspired libraries are cool, and they greatly changes the way you develop, especially signal chaining

Unidirectional data flow

Another trend is React inspired libraries, which encourage Unidirectional data flow

Data manipulation

Leverage some functional style for better data manipulation

  • Swiftz It defines functional data structures, functions, idioms, and extensions that augment the Swift standard library.
  • Dollar.swift A functional tool-belt for Swift Language similar to Lo-Dash or Underscore.js in Javascript

Prefer decoration over extension

This maybe subjective. But in some case decoration wins

Often when we want to add custom color, font or animate a view, or custom NSDateFormatter, we often make categories on UIColor, UIFont, UIView, NSDateFormatter, …

But most of the case, we can group them inside class, like ColorBucket, FontBucket, ViewAnimator, DateFormatterBucket, …

So, instead of

1
2
3
4
5
@interface UIColor (Additions)

- (UIColor *)customColor;

@end

We can have ColorBucket

1
2
3
4
5
@interface ColorBucket

+ (UIColor *)customColor;

@end

Mixin

Another way to add functionality to an entity is mixin, which is cool

IoC container

Usage of IoC container can greatly change the app workflow, which praises Dependency Injection

Single Responsibility Principle

Always keep this in mind :]

Performance

Understanding of performance issue, especially some common uses like tableview pre rendering, helps

Main thread

It’s good to care about our main thread so see if it is blocked

Overlay app icon with build information

Show TODO FIXME as warnings

Use this script in Run Script Build Phase

1
2
KEYWORDS="TODO:|VERIFY:|FIXME:|\?\?\?:|\!\!\!:"
find "${SRCROOT}" -name "*.h" -or -name "*.m" -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($KEYWORDS).*\$" | perl -p -e "s/($KEYWORDS)/ warning: \$1/"

Breakpoints

There are some common needed breakpoints, like Breakpoints_v2.xcbkptlist

Code snippets

Code snippets auto generate common code

Utility class

We can avoid duplicated helper classes in projects by using some cool frameworks

  • Sugar Sugar is a sweetener for your Cocoa implementations.

Xcode plugins

Using Xcode plugins can boost your productivity

And learning to write your own can assist you with more tasks

Xcode keyboard shortcuts

Navigate faster

xcconfig

Build yourself some predefined xcconfig

Strongly typed resource

There’s some scripts that generate strongly typed resource classes

  • SwiftGen A collection of Swift tools to generate Swift code (enums for your assets, storyboards, Localizable.strings, …)
  • R.swift Get strong typed, autocompleted resources like images, fonts and segues in Swift projects

Continuous learning

Technologies move really fast. We must learn continuously. I have ios-resources to keep track of all the cool stuffs

Also, I leverage Github as my notes ios-issues

Reference

Symbolic breakpoints are breakpoints based on symbols like functions or methods.

In Swift

You can set symbolic breakpoints on any free functions, instance and class methods, whether in your own classes or not.

Here is an example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
func freeFunction() {
print("freeFunction")
}

class Robot {
init() {
print("init")
}

func instanceMethod() {
print("instanceMethod")
}

func instanceMethod(noise: Int) {
print("instanceMethod")
}

func instanceMethodWithArguments(a: Int, b: Int) -> Int {
print("instanceMethodWithArguments")
return a + b
}

func instanceMethodWithArguments(a: Int, b: Int, c: Int) -> Int {
print("instanceMethodWithArguments")
return a + b + c
}

class func classMethod() {
print("classMethod")
}

struct MyStruct {
static func staticMethod() {
print("staticMethod")
}
}
}

struct MyStruct {
static func staticMethod() {
print("staticMethod")
}
}

You can have some breakpoints in Swift like

1
2
3
4
5
6
7
UIViewController.viewDidLoad
freeFunction
Robot.instanceMethod
instanceMethodWithArguments
Robot.classMethod
Robot.init
Robot.MyStruct.staticMethod
  • There seems no difference between instance and class methods !!
  • Robot.MyStruct.staticMethod says that it must match staticMethod inside Robot.MyStruct
  • With instanceMethodWithArguments, Xcode Breakpoint panel shows options for (Robot)(Swift.Int)(b: Swift.Int) -> Swift.Int and (Robot)(Swift.Int)(b: Swift.Int)(c: Swift.Int) -> Swift.Int. This may remind you of Instance Methods are Curried Functions in Swift
  • I thought that symbolic breakpoint must match Selector, like instanceMethodWithArguments:b:c, but it is not
  • Objective-C style -[UIViewController viewDidLoad] or -[Robot instanceMethod] does not work !!

And talking about Swift selector, you may want to take a look at this proposal Referencing the Objective-C selector of a method

In Objective-C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void freeFunction() {
NSLog(@"freeFunction");
}


@interface Robot : NSObject

@property (nonatomic, copy) NSString *name;

- (void)instanceMethod;
- (void)instanceMethod:(NSInteger)a b:(NSInteger)b;
+ (void)classMethod;

@end

@implementation Robot

- (instancetype)initWithName:(NSString *)name {
self = [super init];
self.name = name;

return self;
}

- (void)instanceMethod {
NSLog(@"instanceMethod");
}

- (void)instanceMethod:(NSInteger)a b:(NSInteger)b {
NSLog(@"instanceMethod:b:");
}

+ (void)classMethod {
NSLog(@"classMethod");
}

@end

You can have some breakpoints in Objective-C like

1
2
3
4
5
6
-[UIViewController viewDidLoad]
freeFunction
-[Robot initWithName:]
-[Robot instanceMethod]
-[Robot instanceMethod:b:]
+[Robot classMethod]
  • We must specify the correct method with arguments like -[Robot instanceMethod:b:]
  • I use another Swift class into this Objective-C project
1
2
3
4
5
@objc public class Animal: NSObject {
@objc func hello() {
print("hello")
}
}

but the symbolic breakpoint -[Animal hello] does not work. Only Animal.hello works !!

Syntax

  • There are Swift syntax Animal.hello and Objective-C syntax -[Animal hello]. It seems that Swift syntax must be used on Swift files. So in Swift project, we should use Swift syntax
  • init is instance method, see How to swizzle init in Swift

System symbols

Symbolic breakpoints are helpful in case you want to inspect system behaviours

Auto Layout

Breakpoints_v2.xcbkptlist

swift_willThrow

objc_exception_throw

It is your Exception Breakpoint with Break set to Throw

There’s a Break type Catch also

Swizzle

Symbolic breakpoints help when you want to understand system call, like when you try to make Xcode plugin

See How To Create an Xcode Plugin: Part 1/3 on how to break in initWithIcon:message:parentWindow:duration

Assembly

Break on system classes and you want to understand the parameters there, so an understanding of assembly helps

Interesting

Some interesting questions

chisel

Facebook has chisel, which is a collection of lldb commands in Python

Specifically, bmessage

Set a symbolic breakpoint on the method of a class or the method of an instance without worrying which class in the hierarchy actually implements the method.

Reference

Mac OS X has some useful tools that you may overlook. They are useful in your daily development

Simulate network condition. Accessible from System Preferences -> Network Link Conditioner

ColorSync Utility

Pick and convert color. Accessible from Applications/Utilities/ColorSync Utility.app

Automator

Write script to automate task. Accessible from Applications/Automator.app