Function

Functions in Swift are distinguishable by

  • parameter label
  • parameter type
  • return type

so that these are all valid, and works for subscript as well

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
struct A {

// return type
func get() -> String { return "" }
func get() -> Int { return 1 }

// mix of parameter type and return type
func get(param: String) -> String { return "" }
func get(param: String) -> Int { return 1 }
func get(param: Int) -> Int { return 1 }
func get(param: Int) -> String { return "" }

subscript(param: String) -> String { return "" }
subscript(param: String) -> Int { return 1 }
subscript(param: Int) -> Int { return 1 }
subscript(param: Int) -> String { return "" }

// parameter label
func set(int: Int) {}
func set(string: String) {}

// concrete type from generic
func get(param: Array<String>) -> String { return "" }
func get(param: Array<Int>) -> Int { return 1 }

subscript(param: Array<String>) -> String { return "" }
subscript(param: Array<Int>) -> Int { return 1 }
}

When you specialize a generic type, like Array<Int>, you’re actually using a concrete type

Unfortunately, this does not work for NSObject subclass

Method ‘get()’ with Objective-C selector ‘get’ conflicts with previous declaration with the same Objective-C selector

1
2
3
4
5
class B: NSObject {

func get() -> String { return "" }
func get() -> Int { return 1 }
}

Generic function

We can overload generic functions as well

1
2
3
4
5
6
7
8
9
10
11
func f<T>(t: T) {
print("T")
}

func f(string: String) {
print("String")
}

func f(int: Int) {
print("Int")
}

This post is like a sum up of sum ways to configure your property

Using a helper method
1
2
3
4
5
6
7
8
9
10
11
12
13
var label: UILabel!

override func viewDidLoad() {
super.viewDidLoad()

configureLabel()
}

func configureLabel() {
label = UILabel()
label.backgroundColor = UIColor.greenColor()
view.addSubview(label)
}
Using anonymous function
1
2
3
4
5
lazy var label: UILabel = { [weak self] in
let label = UILabel()
label.backgroundColor = UIColor.greenColor()
return label
}()

Ah, by the way, did you know that

  • You shouldn’t call access label in ViewController deinit, because it is lazy and we have weak self
  • lazy increases your compile time
@noescape configure Block on init

I first saw it on https://github.com/AliSoftware/Dip/blob/develop/Sources/Dip.swift#L61

1
2
3
public init(@noescape configBlock: (DependencyContainer->()) = { _ in }) {
configBlock(self)
}
configure Block as extension

This https://github.com/devxoul/Then makes it easier to configure your property as an extension to NSObject

1
2
3
4
5
6
7
extension Then where Self: AnyObject {

public func then(@noescape block: Self -> Void) -> Self {
block(self)
return self
}
}

so we have

1
2
3
lazy var label: UILabel = UILabel().then { [weak self] in
$0.backgroundColor = UIColor.greenColor()
}

We have to declare label: UILabel to use `[weak self]

init without extension

I try to avoid extension, after reading this http://nshipster.com/new-years-2016/

1
2
3
4
5
public func Init<Type>(value : Type, @noescape block: (object: Type) -> Void) -> Type
{
block(object: value)
return value
}

we can use it like

1
2
3
lazy var label: UILabel = Init(UILabel()) { [weak self] in
$0.backgroundColor = UIColor.greenColor()
}

We have to declare label: UILabel to use `[weak self]

anonymous function again

This https://gist.github.com/erica/4fa60524d9b71bfa9819 makes configuration easier

1
2
3
4
lazy var label: UILabel = { [weak self] in
$0.backgroundColor = UIColor.greenColor()
return $0
}(UILabel())

GitHub is so awesome. It is where people around the world collaborate with each other.

It is more awesome to show more about you in your GitHub profile. How about a badge? a welcome text?

It is doable with organization. GitHub takes time and name of the organiazations you joined to determined how it displays on your profile

This is what shown on my GitHub profile https://github.com/onmyway133

For me, I display the text “Hello World”, so I have to create organizations for “h”, “e”, “l”, “l”, “o”, “w”, “o”, “r”, “l”, “d”

To ensure the order, you can name your organization like “org-h”, “org-he”, “org-hel”, “org-hell”, “org-hello”, … and you must join the organization in the correct order

I create another GitHub account called https://github.com/fantabot to manage my organizations

Your imaginary is your limit. May your code continue to compile :grin:

Swift allows us to define more methods on existing class using extension.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension UIView {

func shake() {

}

func fade() {

}

func centerIn(anotherView: UIView) {

}
}

If you ‘re afraid of the naming conflict, you can prefix your methods. Or a better way, reverse it :dancer: , like

1
2
3
view.animation.shake()
view.animation.fade()
view.layout.centerIn(anotherView)

This way, no more conflict and we make it clear that shake() and fade() belongs to animation category

Actually, animation and layout are properties in UIView extension. This may cause naming conflict, but the number of them is reduced

This is how it works

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
extension UIView {

struct Animation {
let view: UIView

func shake() {
// Shake the view
}

func fade() {
PowerfulAnimationEngine.fade(view)
}
}

var animation: Animation {
return Animation(view: self)
}

struct Layout {
let view: UIView

func centerIn(anotherView: UIView) {

}
}

var layout: Layout {
return Layout(view: self)
}
}

This is applied in Wave

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
}
}