Giter Club home page Giter Club logo

blog's Introduction

blog's People

Contributors

onmyway133 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

How to make lighter AppDelegate in iOS

There is Lighter View Controllers, and there is Lighter AppDelegate, too

Since working with iOS, I really like the delegate pattern, in which it helps us defer the decision to another party.

The iOS application delegates its event to AppDelegate, which over time will be a big mess. Usually, the AppDelegate is where you put your root view controller setup, crash tracking, push notification, debugging, ... and we just somehow violent the Single Responsibility principle. Moreover, it makes us hard to reason about code in AppDelegate

Service

I like to think of each task in AppDelegate as a service. And the AppDelegate distributes the events into each service via ServiceDispatcher. Simple plain old composition and looping

I tend to have RootService as a place to setup root view controllers

It looks like this

ServiceDispatcher.swift

class ServiceDispatcher : NSObject, UIApplicationDelegate {
    let services: [UIApplicationDelegate]

    init(services: [UIApplicationDelegate]) {
        self.services = services
    }

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

        services.forEach { service in
            service.application?(application, didFinishLaunchingWithOptions: launchOptions)
        }

        return true
    }

    func applicationDidBecomeActive(application: UIApplication) {
        services.forEach { service in
            service.applicationDidBecomeActive?(application)
        }
    }

    func applicationWillResignActive(application: UIApplication) {
        services.forEach { service in
            service.applicationWillResignActive?(application)
        }
    }

    func applicationWillEnterForeground(application: UIApplication) {
        services.forEach { service in
            service.applicationWillEnterForeground?(application)
        }
    }

    func applicationDidEnterBackground(application: UIApplication) {
        services.forEach { service in
            service.applicationDidEnterBackground?(application)
        }
    }
}

RootService.swift

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

        appDelegate().window = UIWindow(frame: UIScreen.mainScreen().bounds)
        showHome()
        appDelegate().window?.makeKeyAndVisible()

        return true
    }
}

extension RootService {
    func showHome() {
        let home = HomeWireframe().makeHome()
        let navC = UINavigationController(rootViewController: home!)
        appDelegate().window?.rootViewController = navC
    }
}

extension RootService {
    func appDelegate() -> AppDelegate {
        return UIApplication.sharedApplication().delegate as! AppDelegate
    }
}

AppDelegate.swift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let serviceDispatcher = ServiceDispatcher(services: [RootService()])


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

        serviceDispatcher.application(application, didFinishLaunchingWithOptions: launchOptions)

        return true
    }

    func applicationWillResignActive(application: UIApplication) {
        serviceDispatcher.applicationWillResignActive(application)
    }

    func applicationDidEnterBackground(application: UIApplication) {
        serviceDispatcher.applicationDidEnterBackground(application)
    }

    func applicationWillEnterForeground(application: UIApplication) {
        serviceDispatcher.applicationWillEnterForeground(application)
    }

    func applicationDidBecomeActive(application: UIApplication) {
        serviceDispatcher.applicationDidBecomeActive(application)
    }
}

I have more services like DebugService, PushNotificationService, CrashTrackingService, ...

The downside to this approach is that in real life, there will be dependencies between those services, like that UserService must be called before RootService? In this case, I have to use comment to explain why I have that decision, which is hard for newcomers to understand at first. Take a look at How to Move Bootstrapping Code Out of AppDelegate for how dependencies are managed

JSDecoupledAppDelegate comes with another approach, in which service events are named according to the functions, like appStateDelegate, appDefaultOrientationDelegate, watchInteractionDelegate, ...

But for me, Service and ServiceDispatcher suit my need

Reference

Hello world, again

I've used Wordpress, then moved to GitHub Pages with Jekyll, Octopress, Hexo, Hugo. You can view my page here http://fantageek.com/. It was good with all the custom themes and Disqus

But then I was a bit lazy with all the commands generate, commit, deploy, it hinders me from writing, so I moved to Medium.

The only thing I like about Medium is its discovery, your posts have high chanced of finding and viewing by people. What's the point of writing if no one read it? But then I miss all the awesome markdown features of GitHub Pages. Medium is easy to use, but it seems it's not for hackers, and I find it really uncomfortable when adding code block and headings. Medium also lists my comments as stories, which is kind of 😲

I like to write fast, and with good comments system, and I love Markdown.

I like GitHub. I use GitHub for my notes, so I think I will use it for my blog as well. Hope all these GitHub convenience will encourage me to write more often. This will, of course, be less discoverable by people. So if you by any chance visit this blog, ohayou from me 👋

How to create a piano using iOS 9 Auto Layout

In the beginning, people use frame and Autoresizing Mask, then they use Auto Layout, then iOS 9 encourages them to use NSLayoutAnchor, UILayoutGuide and UIStackView

For more convenient Auto Layout, check How to make Auto Layout more convenient in iOS and Anchors

NSLayoutAnchor

The NSLayoutAnchor class is a factory class for creating NSLayoutConstraint objects using a fluent API. Use these constraints to programmatically define your layout using Auto Layout.

It has 3 subclasses

NSLayoutDimension

  • func constraintEqualToConstant(_ c: CGFloat) -> NSLayoutConstraint!

NSLayoutXAxisAnchor

  • Allows working with horizontal constraints
  • Prevent these
// This constraint generates an incompatible pointer type warning
cancelButton.leadingAnchor.constraintEqualToAnchor(saveButton.topAnchor, constant: 8.0).active = true

NSLayoutYAxisAnchor

  • Allows working with vertical constraints
  • Prevent these
// This constraint generates an incompatible pointer type warning
cancelButton.topAnchor.constraintEqualToAnchor(saveButton.trailingAnchor, constant: 8.0).active = true

UILayoutGuide

Previously, we used dummy views to aid constraints. Now we use UILayoutGuide

Define an equal spacing between a series of views

uilayoutguide_spacing

See full gist

let space1 = UILayoutGuide()
view.addLayoutGuide(space1)

let space2 = UILayoutGuide()
view.addLayoutGuide(space2)

space1.widthAnchor.constraintEqualToAnchor(space2.widthAnchor).active = true

saveButton.trailingAnchor.constraintEqualToAnchor(space1.leadingAnchor).active = true

cancelButton.leadingAnchor.constraintEqualToAnchor(space1.trailingAnchor).active = true
cancelButton.trailingAnchor.constraintEqualToAnchor(space2.leadingAnchor).active = true

clearButton.leadingAnchor.constraintEqualToAnchor(space2.trailingAnchor).active = true

Layout guides can also act as a black box, containing a number of other views and controls

uilayoutguide_container

See the full gist

let container = UILayoutGuide()
view.addLayoutGuide(container)

// Set interior constraints
label.lastBaselineAnchor.constraintEqualToAnchor(textField.lastBaselineAnchor).active = true
label.leadingAnchor.constraintEqualToAnchor(container.leadingAnchor).active = true

textField.leadingAnchor.constraintEqualToAnchor(label.trailingAnchor, constant: 8.0).active = true
textField.trailingAnchor.constraintEqualToAnchor(container.trailingAnchor).active = true

textField.topAnchor.constraintEqualToAnchor(container.topAnchor).active = true
textField.bottomAnchor.constraintEqualToAnchor(container.bottomAnchor).active = true

// Set exterior constraints
// The contents of the container can be treated as a black box
let margins = view.layoutMarginsGuide

container.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor).active = true
container.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true

// Must use NSLayoutConstraint with the scene's top and bottom layout guides.
NSLayoutConstraint(item: container,
    attribute: .Top,
    relatedBy: .Equal,
    toItem: topLayoutGuide,
    attribute: .Bottom,
    multiplier: 1.0,
    constant: 20.0).active = true

layoutMarginsGuide

Margins are now represented as layoutMarginsGuide, a subclass of UILayoutGuide

topLayoutGuide and bottomLayoutGuide

In the container example, we saw how we must use NSLayoutConstraint with the topLayoutGuide. topLayoutGuide and bottomLayoutGuide are object conforming to UILayoutSupport protocol

layoutFrame

The layout guide defines a rectangular space in its owning view’s coordinate system. This property contains a valid CGRect value by the time its owning view’s layoutSubviews method is called.

In the above container example, the container layout guide frame is

(16.0, 40.0, 343.0, 21.0)

Piano

piano

See Piano on Github on how to create a Piano using UILayoutGuide, NSLayoutAnchor and UIStackView

Disingenuousness

I'm very happy to be on open source movement, and it 'll be great to hear about what people have achieved

And @merowing_ also mentioned in Writing Xcode plugin in Swift

Attribution

Writing this was much simpler because I was able to look at other people plugins, mostly those related to console, without them being open sourcing it would be more work to figure this stuff out with hopper.

Open source helps us move forward, learn and share together

The dark side of the Force

Luke: Is the dark side stronger?

Yoda: No, no, no. Quicker, easier, more seductive.

It's a pain to see plagiarism around

Open source softwares are in fact intellectual properties, and the authors should get acknowledgement for the work that they do.

It's not fair to take the credit of other's work and not giving any attribution

By its nature, open source software has a unique relationship with intellectual property rights

One thing that's not up for debate in most circles is that it's dishonest and disingenuous to take someone else's project, modify it slightly, and call it your own.

Further, regardless of whether or not a project crosses that line, it must (by the terms of most open source licenses) acknowledge the original work/author.

And the reaction

It's always sad to see blatant plagiarism, and I think it really hurts the community more than the author itself. It gives people a good reason to keep the sources private.

Being nice

I often hear people say that

It is easier to find good developer than developer with good attitude

Github also states that

We understand and agree that copying others' work without permission goes against the spirit of the open source community

Do the right things

Is it MIT 's fault? Definitely no

False choice. Giving up freedom does not lead to more security, just less freedom.

Takeaways

  • Don't take things personally
  • It's hard to be perfect, but we can learn to do the right things
  • We may do the wrong things, but don't worry, there 'll be help via Issues and Pull Requests

Composition in Realm

There is time we have models that have some kind of inheritance, like Dog, Cat, Mouse can be Animal. We can use composition to imitate inheritance, we just need to make sure it has unique primary key

Cat and Dog

These are pretty much basic Realm objects

class Dog: Object {
  dynamic var id: Int = 0

  required convenience init(json: [String: Any]) {
    self.init()
    id <- json.integer(key: "id")
  }
}

class Cat: Object {
  dynamic var id: Int = 0

  required convenience init(json: [String: Any]) {
    self.init()
    id <- json.integer(key: "id")
  }
}

Animal

Here Animal can contain either dog or cat, we can add more if there are many other "inheritance" of Animal. The required init makes sure Animal can only be init with 1 type of child class

class Animal: Object {
  dynamic var id: Int = 0

  dynamic var cat: Cat?
  dynamic var dog: Dog?

  required convenience init(cat: Cat) {
    self.init()
    self.cat = cat
    self.id = cat.id
  }

  required convenience init(dog: Dog) {
    self.init()
    self.dog = dog
    self.id = dog.id
  }
}

Understanding push and pull signal in reactive paradigm

The idea of Signal may originate from Elm Reactivity, and it has now been widely adopted in iOS

I once asked What are examples of hot and cold signal in ReactiveCocoa?

Whether it is hot vs cold, Signal vs Signal Producer, Observable vs Enumerable, ... it's good to understand how it gets implemented, so that to have a good sense of how they work

Monad

Basically, Signal and its Result are just monads, which are thing that can be mapped and chained.

Signal makes use of deferred execution callback blocks, and push vs pull is just how the Signal updates its value and the order the callbacks are called

Execution callback block is that we pass a function to another function, and it will get called when appropriated

Sync vs Async

Monad can be in either sync or async mode. Sync is easier to understand, but async is somewhat you're already familiar and used in practice

Basically,

  • Sync: you get the returned value right away via return
  • Aync: you get the returned value via callback block

Here is an example of a simple function

// Sync
func sum(a: Int, b: Int) -> Int {
    return a + b
}

// Async
func sum(a: Int, b: Int, completion: Int -> Void) {
    // Assumed it is a very long task to get the result
    let result = a + b

    completion(result)
}

Here is an example of Event

// Sync
public func map<U>(@noescape f: T -> U) -> Event<U> {
    switch self {
    case let .Next(value):
        return .Next(value: f(value))
    case let .Failed(error):
        return .Failed(error: error)
    }
}

// Async
public func map<U>(f: (T, U -> Void) -> Void) -> ((Event<U> -> Void) -> Void) {
        return { g in   // g: Event<U> -> Void
            switch self {
            case let .Next(value):
                f(value) { transformedValue in  // transformedValue: U
                    g(.Next(value: transformedValue))
                }
            case let .Failed(error):
                g(.Failed(error: error))
            }
        }
    }

Push Signal

Take a look at my Push Signal, called Signal, it is like how Promise A+ Then works

Implementation

public final class Signal<T> {
    var event: Event<T>?
    var callbacks: [Event<T> -> Void] = []

    func notify() {
        guard let event = event else {
            return
        }

        callbacks.forEach { callback in
            callback(event)
        }
    }

    func update(event event: Event<T>) {
        dispatch_sync(lockQueue) {
            self.event = event
        }

        notify()
    }

    public func subscribe(f: Event<T> -> Void) -> Signal<T> {
        // Callback
        if let event = event {
            f(event)
        }

        callbacks.append(f)

        return self
    }

    public func map<U>(f: T -> U) -> Signal<U> {
        let signal = Signal<U>()

        subscribe { event in
            signal.update(event: event.map(f))
        }

        return signal
    }
}

Usage

let signal = Signal<String>()

signal.map { value in
        return value.characters.count
    }.subscribe { event in
        if case let .Next(value) = event {
            XCTAssert(value == 4)
        } else {
            XCTAssert(false)
        }
}

signal.sendNext("test")

Callbacks

Given a chained signals like this

A -(map)-> B -(flatMap)-> C -(flatMap)-> D -(subscribe)

  • The idea is we send event to the source signal, and it propagates events through via callbacks.
  • Triggered by sending event to the source signal.
  • We must keep A as it keeps the others around
  • We subscribe the last D
  • We send event to the first A
  • A 's callback gets called, it it in turn calls callback of B with the result of A 's map, then B 's callback calls C 's callback with the result of B
    's flatMap, ...

Pull Signal

Take a look at my Pull Signal, called Future

Implementation

Here operation is a task, when called and completed, will notify its completion

public struct Future<T> {
    let operation: (Event<T> -> Void) -> Void

    public init(operation: (Event<T> -> Void) -> Void) {
        self.operation = operation
    }

    public func start(completion: Event<T> -> Void) {
        operation() { event in
            completion(event)
        }
    }

    public func map<U>(f: T -> U) -> Future<U> {
        return Future<U> { completion in
            self.start { event in
                completion(event.map(f))
            }
        }
    }
}

Usage

let _ = Future<String> { completion in
        // There is some work here
        completion(Event(value: "test"))
    }
    .map { value in
        value.characters.count
    }.start { event in
        if case let .Next(value) = event {
            XCTAssert(value == 4)
        } else {
            XCTAssert(false)
        }
    }

Callbacks

Given a chained signals like this

A -(map)-> B -(flatMap)-> C -(flatMap)-> D -(subscribe)

  • The idea is we subscribe to the final signal D, and it cause the previous signals to action.
  • Triggered by subscribing to the final signal.
  • We must keep D as it keeps the others around
  • We subscribe the last D
  • D 's operation actions, and it cause C 's operation to action, ... then A 's operation actions. It is in A that the task is performed (like fetching network, retrieving database, file access, heavy computation, ...) to get the result, and A 's completion gets called. Then A's completion calls B 's completion with the result mapped by B 's map, ... all the way to the subscriber 's completion block

Primary key in Realm

Realm is great. But without primary key, it will duplicate the record, like realm/realm-java#2730, http://stackoverflow.com/questions/32322460/should-i-define-the-primary-key-for-each-entity-in-realm, ... So to force ourselves into the good habit of declaring primary key, we can leverage Swift protocol

Create primary constrain protocol like this

protocol PrimaryKeyAware {
  var id: Int { get }
  static func primaryKey() -> String?
}

and conform it in out Realm object

class Profile: Object, PrimaryKeyAware {

  dynamic var firstName: String = ""
  dynamic var lastName: String = ""
  dynamic var id: Int = 0

  override static func primaryKey() -> String? {
    return "id"
  }
}

This way, when using that object in out RealmStorage, we are safe to say that it has a primary key

class RealmStorage<T: Object> where T: PrimaryKeyAware {
  let realm: Realm

  init(realm: Realm = RealmProvider.realm()) {
    self.realm = realm
  }

  func save(_ objects: [T]) {
    try? realm.write {
      realm.add(objects, update: true)
    }
  }
}

The usage is like this

let profile = Profile()
let storage = RealmStorage<Profile>()
storage.save([profile])

How to use push notification in iOS

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+

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

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

{
    "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

{
 "aps": {
   "content-available": 1,
   "alert": "hello" // include this if we want to show alert
   "sound": "" // this does the trick
  },
  "param1": 1,
  "param2": "text"
}

Reference

Check before you commit

Original post https://medium.com/@onmyway133/check-before-you-commit-5a7601cffc87


We usually have some experiment code that we don’t want they to step into our commit. I usually mark my experiment with // but sometimes forget to unstage that
Starting with 2.9, Git has improvement on its commit hook which makes it globally using hooksPath

Create pre-commit file

Create a file called pre-commit, and place it into, for example /Users/khoa/hooks

#!/bin/sh

# https://appventure.me/2016/04/04/prevent-accidental-test-code-commits/

if git rev-parse --verify HEAD >/dev/null 2>&1
then
  against=HEAD
else
  # Initial commit: diff against an empty tree object
  against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# The special marker tag to mark things which we still need to change
marker="<TEST>"

# Redirect output to stderr.
exec 1>&2

if test $(git diff --cached -z $against | grep $marker | wc -c) != 0 
then
    cat <<\EOF
    Error: Still has invalid debug markers in code:
EOF
    echo `git diff --cached -z $against -G $marker`
    exit 1
fi

Apply the hook

In your project, run git config core.hooksPath /Users/khoa/hooks
That’s it. Whenever you commit a file with that pattern, it won’t let you commit

How to use

Try

// <TEST>
UserManager.shared.isActive = true

and git commit -m "my commit message" will assert with Error: Still has invalid debug markers in code:

Reference

How to define SDK and Deployment Target in iOS

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

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

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

#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
#if os(OSX)
    import Cocoa
#elseif os(iOS)
    import UIKit
#endif
// 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

How to initialize Enums With Optionals in Swift

Today someone showed me https://medium.com/@_Easy_E/initializing-enums-with-optionals-in-swift-bf246ce20e4c which tries to init enum with optional value.

enum Planet: String {
  case mercury
  case venus
  case earth
  case mars
  case jupiter
  case saturn
  case uranus
  case neptune
}

extension RawRepresentable {
  init?(optionalValue: RawValue?) {
    guard let value = optionalValue else { return nil }
    self.init(rawValue: value)
  }
}

let name: String? = "venus"
let planet = Planet(optionalValue: name)

One interesting fact about optional, is that it is a monad, so it has map and flatMap. Since enum init(rawValue:) returns an optional, we need to use flatMap. It looks like this

let name: String? = "venus"
let planet = name.flatMap({ Planet(rawValue: $0) })

🎉

How to use Controller and View in iOS

I like to write UI in code, and with Auto Layout, it is an easy task. However that leaves ViewController with a lots of code. One way we can do is to separate V from C in MVC, by using a dedicated view

We can do that with generic, that initialises a view and replace the view, let's call it root

import UIKit

class BaseController<T: UIView>: UIViewController {
  let root = T()

  override func loadView() {
    view = root
  }
}

Now we can have a UIView subclass, like LoginView

final class LoginView: UIView {
 lazy var textField: UITextField = UITextField().then {
    $0.textAlignment = .center
    $0.borderStyle = .roundedRect
    $0.keyboardType = .phonePad
  }

  lazy var button: UIButton = UIButton().then {
    $0.setTitleColor(.black, for: .normal)
    $0.backgroundColor = .lightGray
  }

  override init(frame: CGRect) {
    super.init(frame: frame)

    addSubviews(
      textField,
      button
    )

    Constraint.on(
      textField.centerXAnchor.constraint(equalTo: textField.superview!.centerXAnchor),
      textField.centerYAnchor.constraint(equalTo: textField.superview!.centerYAnchor),
      textField.widthAnchor.constraint(equalTo: textField.superview!.widthAnchor, constant: -20),

      button.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20),
      button.centerXAnchor.constraint(equalTo: button.superview!.centerXAnchor),
      button.widthAnchor.constraint(equalTo: textField.widthAnchor, multiplier: 0.8),
      button.heightAnchor.constraint(equalToConstant: 44)
    )
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError()
  }
}

And then the LoginController

final class LoginController: BaseController<LoginView> {
  override func viewDidLoad() {
    super.viewDidLoad()

    view.backgroundColor = .white

    let gr = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
    root.addGestureRecognizer(gr)

    root.button.setTitle("Login", for: .normal)
    root.button.addTarget(self, action: #selector(loginButtonTouched), for: .touchUpInside)
    root.button.isEnabled = false
    root.button.showsTouchWhenHighlighted = true

    root.textField.placeholder = "Phone number"
    root.textField.delegate = self
    root.textField.text = dependencyContainer.phoneService.prefix
    root.textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
  }
}

And this is how we declare the LoginController

let loginController = LoginController()
navigationController.viewControllers = [loginController]

How to do delegate with RxSwift

We can use DelegateProxy and DelegateProxyType to make beautiful delegate with RxSwift. But in some other cases, we can just create a custom class with PublishSubject.

This is how we can make rx out of UIApplication life cycle events

class LifeCycle {
  let didEnterBackground = PublishSubject<Void>()
  let willEnterForeground  = PublishSubject<Void>()
  let didBecomeActive = PublishSubject<Void>()
  let willResignActive = PublishSubject<Void>()

  init() {
    let center = NotificationCenter.default
    let app = UIApplication.shared

    center.addObserver(forName: Notification.Name.UIApplicationDidEnterBackground,
                       object: app, queue: .main, using: { [weak self] _ in
      self?.didEnterBackground.onNext(())
    })

    center.addObserver(forName: Notification.Name.UIApplicationWillEnterForeground,
                       object: app, queue: .main, using: { [weak self] _ in
      self?.willEnterForeground.onNext(())
    })

    center.addObserver(forName: Notification.Name.UIApplicationDidBecomeActive,
                       object: app, queue: .main, using: { [weak self] _ in
      self?.didBecomeActive.onNext(())
    })

    center.addObserver(forName: Notification.Name.UIApplicationWillResignActive,
                       object: app, queue: .main, using: { [weak self] _ in
      self?.willResignActive.onNext(())
    })
  }
}

Usage

let lifeCycle = LifeCycle()
lifeCycle.didBecomeActive
      .bindNext({ [weak self] in
        self?.viewModel.reloadProfile()
      })
      .disposed(by: bag)

Law of Jante

The other day I was watching Inside Sweden’s Silicon Valley, and he mentions Law of Jante

It is pretty much this

  • You’re not to think you are anything special.
  • You’re not to think you are as good as we are.
  • You’re not to think you are smarter than we are.
  • You’re not to convince yourself that you are better than we are.
  • You’re not to think you know more than we do.
  • You’re not to think you are more important than we are.
  • You’re not to think you are good at anything.
  • You’re not to laugh at us.
  • You’re not to think anyone cares about you.
  • You’re not to think you can teach us anything.

This is controversial, there are many discussions about this

Putting on your black hat, it sounds negative. Putting on your yellow hat, it sounds positive
But what I learn about it is the team work. No one lives alone, everyone lives among the others. It is about to be humble and learn collaboration

NSApplicationDelegate and notification

In an iOS project, we often see this in AppDelegate

@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

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

How to use assertionFailure and Optimization Level in iOS

We used to use assertionFailure to mark programmer error or something that shouldn't happen.

From assertionFailure

Indicates that an internal sanity check failed.

Use this function to stop the program, without impacting the performance of shipping code, when control flow is not expected to reach the call—for example, in the default case of a switch where you have knowledge that one of the other cases must be satisfied. To protect code from invalid usage in Release builds, see preconditionFailure(_:file:line:).

  • In playgrounds and -Onone builds (the default for Xcode’s Debug configuration), stop program execution in a debuggable state after printing message.
  • In -O builds, has no effect.
  • In -Ounchecked builds, the optimizer may assume that this function is never called. Failure to satisfy that assumption is a serious programming error.

So go to your target settings, and check Optimization Level, make sure it is not -Onone for release configuration.

The difference between debug and release is this SWIFT_OPTIMIZATION_LEVEL. If -Onone then your configuration is considered debug, and assertionFailure will crash your apps

Read more

Advices to students

Some say these are from the book Dumbing Down America of Charles Sykes, some say they are from Bill Gates 's speech to high school students. I don't know the origin, but they are cool enough, so I tend to share it again and again

  • Life is not fair. Get used to it ⭐️
  • The world won't care about your self-esteem. The world will expect you to accomplish something BEFORE you feel good about yourself
  • You will NOT make 40 thousand dollars a year right out of high school. You won't be a vice president with car phone, until you earn both.
  • If you think your teacher is tough, wait till you get a boss. He doesn't have tenure.
  • If you mess up, it's not your parents' fault, so don't whine about your mistakes, learn from them.
  • Your school may have done away with winners and losers, but life may not. In some schools they have abolished failing grades and they'll give you as many times as you want to get the right answer. This doesn't bear the slightest resemblance to ANYTHING in real life.
  • Life is not divided into semesters. You don't get summers off and very few employers are interested in helping you find yourself. Do that on your own time.
  • Television is NOT real life. In real life people actually have to leave the coffee shop and go to jobs.
  • Be nice to nerds. Chances are you'll end up working for one.
  • Before you were born your parents weren't as boring as they are now. They got that way paying your bills, cleaning up your room and listening to you tell them how idealistic you are. And by the way, before you save the rain forest from the blood-sucking parasites of your parents' generation, try delousing the closet in your bedroom ⭐️

Reference

What you don't know is what you haven't learned

Some of my sayings that I like 😉

Mine

  • There are so many many many things to learn
  • The opportunities are endless
  • Everybody has access to the internet. Everybody has nearly the same chances. Your future is yours to decide
  • A dream is not a dream anymore when you don't have time for it
  • Being single means free time. Being coupled means memorable time
  • You learn less with the happy path
  • I don't like to be at the centre, nor being abandoned
  • Even sense of humour can be trained
  • Youth is your strength. Take advantage of it
  • Easy to please. Hard to impress
  • Don't show me the evil sides of the world
  • Please don't confuse peace vs boredom
  • What matters is your passion, not your ability
  • The ones that are likely to fail are the ones having the fewest friends. And the ones that have the fewest friends are the ones having the most boring life
  • Life is like a marathon. People run. Some are lucky enough to have support. Some are even luckier, they already crossed the finish line when they were born. Running, however, has its own fun
  • Life is predestined. But you can of course change it
  • Losers are easily the most hot tempered. It is the consequence, not the cause
  • What if there is no inheritance. Will that make everybody the same?
  • Some people become Norwegian when they were born. Others have to apply for the citizenship https://www.udi.no/en/word-definitions/norwegian-by-birth/
  • Every deck has a chance to win, as long as you believe in the heart of the cards
  • Life is a game. People play on different difficulty levels. "But mine is much harder", said everybody
  • What you don't know is what you haven't learned

Others

  • I don't know the key to success. But the key to failure is trying to please everybody ~ Bill Cosby

How to run UITests with map view in iOS

Mock a location

You should mock a location to ensure reliable test

Create the gpx file

Go to Xcode -> File -> New -> GPX File

gpx

It looks like

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
  <wpt lat="59.913590" lon="10.733750">
    <name>Oslo S</name>
    <time>2017-05-31T14:55:37Z</time>
  </wpt>
  <wpt lat="59.913590" lon="10.733750">
    <name>Oslo S</name>
    <time>2017-05-31T14:55:40Z</time>
  </wpt>
</gpx>

The gpx file is very powerful, as it allows you to specify a route with different movement speed.

Provide one or more waypoints containing a latitude/longitude pair. If you provide one
waypoint, Xcode will simulate that specific location. If you provide multiple waypoints,
Xcode will simulate a route visiting each waypoint.

Optionally provide a time element for each waypoint. Xcode will interpolate movement
at a rate of speed based on the time elapsed between each waypoint. If you do not provide
a time element, then Xcode will use a fixed rate of speed. Waypoints must be sorted by time in ascending order.

Use the gpx file

  • Declare the gpx file in app target, not UITests target. Go to your app scheme -> Run -> Options

location

  • Go to Simulator -> Debug -> Location -> Custom Location and select that same location, just to make sure. It does not need to be the same, but I see that without Custom Location, it does not work in UITests

Test that you're near the initial location

let map = app.maps.element(boundBy: 0)
let predicate = NSPredicate(format: "label CONTAINS 'City Hall'")
let cityHall = map.otherElements.matching(predicate).element(boundBy: 0)

// wait for the map to finish loading and zooming
wait(for: cityHall, timeout: 2)
XCTAssertTrue(cityHall.exists)

The wait function is from #44

Test that you can interact with your custom pin

You need to specify accessibilityIdentifier, like

class MyPin: MKAnnotationView {
  override func didMoveToSuperview() {
    super.didMoveToSuperview()

    accessibilityIdentifier = "myPin"
  }
}

and then query for that pin. Not that it is not inside map, it is inside app

let pin = app.otherElements.matching(identifier: "myPin").element(boundBy: 0)
XCTAssertTrue(pin.exists)

You should use accessibilityIdentifier

accessibilityIdentifier is from UIAccessibilityIdentification protocol. You should not use accessibilityLabel, see kif-framework/KIF#243

Given that accessibilityLabel is an outwardly-facing string that is actually used by accessibility screen readers (and should be localized to the device user's language), Apple now provides an alternate property (iOS 5+) that is specifically intended for UI Automation purposes

App backed by website in iOS 9

iOS 9 introduces new ways for your app to work better, backed by your websites

Smart App Banners

If the app is already installed on a user's device, the banner intelligently changes its action, and tapping the banner will simply open the app. If the user doesn’t have your app on his device, tapping on the banner will take him to the app’s entry in the App Store

To add a Smart App Banner to your website, include the following meta tag in the head of each page where you’d like the banner to appear:

<meta name="apple-itunes-app" content="app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL">

Universal Links

When you support universal links, iOS 9 users can tap a link to your website and get seamlessly redirected to your installed app without going through Safari. If your app isn’t installed, tapping a link to your website opens your website in Safari.

Web Markup

If some or all of your app’s content is also available on your website, you can use web markup to give users access to your content in search results. Using web markup lets the Applebot web crawler index your content in Apple’s server-side index, which makes it available to all iOS users in Spotlight and Safari search results.

Shared Web Credentials

Shared web credentials is a programming interface that enables native iOS apps to share credentials with their website counterparts. For example, a user may log in to a website in Safari, entering a user name and password, and save those credentials using the iCloud Keychain. Later, the user may run a native app from the same developer, and instead of the app requiring the user to reenter a user name and password, shared web credentials gives it access to the credentials that were entered earlier in Safari.

Reference

How to use MainController in iOS

Usually in an app, we have these flows: onboarding, login, main. And we usually set OnboardingController, LoginController and MainController as the root view controller respectively depending on the state.

I find it useful to have the MainController as the container for main flow. It can be a tab controller, swipe menu controller or contains just 1 child view controller. The screens are provided by child view controllers, but the MainController does the following jobs

  • Status bar style

We usually need to call preferredStatusBarStyle on the parent controller. See https://stackoverflow.com/questions/19022210/preferredstatusbarstyle-isnt-called

  • App did become active

Usually when app is brought to foreground, we need to fetch logged in user profile to see if there's changes. We do this by listening to app did become active in MainController.

  • Mock to open

This can be anti pattern. But in UI Tests, for laziness, we can just use some launch arguments and check to present some specific screens to test, because MainController is the root for main flow.

  • Logout

Because things originate from MainController, things can terminate in MainController. We can handle logout, clear states, and tell MainController to tell AppDelegate to switch to another root controller

Configuration closure in Swift

When I was reading through Swinject, I found something interesting https://github.com/Swinject/Swinject/blob/master/Sources/Container.swift

public convenience init(parent: Container? = nil, registeringClosure: (Container) -> Void) {
  self.init(parent: parent)
  registeringClosure(self)
}

The init has a closure that makes configuration easy, much like a Builder pattern. So I think we can learn from that and make a Configurable protocol

protocol Configurable: class {}

extension Configurable {
  func config(block: (Self) -> Void) -> Self {
    block(self)
    return self
  }
}

extension NSObject : Configurable {}

With this, we can init some class with less hassle

let view = UIView().config {
  $0.backgroundColor = .white
  $0.layer.cornerRadius = 2
}

Swift snippets

I always forget how to write correct #available( or #if swift(>=3.0) or just lazy to write required init?(coder aDecoder: NSCoder) every time I make a subclass. That's why I made SwiftSnippets to save time for these tedious tasks. Installation is easy with script, so you should give it a try.

I can't recommend this enough, it saves me tons of time

How to perform platform check with typealias and @available

The other day, I was building Anchors which needs to support iOS and macOS. What's clever way to not use #if os(iOS) || os(tvOS) in all files? Use typealias

This is the first version. I'm trying to support iOS 8, macOS 10.10

#if os(iOS) || os(tvOS)
  import UIKit
  public typealias View = UIView
  public typealias LayoutGuide = UILayoutGuide
  public typealias EdgeInsets = UIEdgeInsets
#elseif os(OSX)
  import AppKit
  public typealias View = NSView
  public typealias LayoutGuide = NSLayoutGuide
#endif

But then because of LayoutGuide, I need to bump deployment target to iOS 9, macOS 10.11. Which is not what I want. @available to the rescue, but it will affect everything below it. The solution is to split the platform check, the first as normal, the second with @available check

#if os(iOS) || os(tvOS)
  import UIKit
  public typealias View = UIView
  public typealias EdgeInsets = UIEdgeInsets
#elseif os(OSX)
  import AppKit
  public typealias View = NSView
#endif

#if os(iOS) || os(tvOS)
  import UIKit
  @available(iOS 9.0, *)
  public typealias LayoutGuide = UILayoutGuide
#elseif os(OSX)
  import AppKit
  @available(macOS 10.11, *)
  public typealias LayoutGuide = NSLayoutGuide
#endif

How to handle Auto Layout with different screen sizes

Auto Layout is awesome. Just declare the constraints and the views are resized accordingly to their parent 's bounds changes. But sometimes it does not look good, because we have fixed values for padding, width, height, and even fixed font size.

Read more How to make Auto Layout more convenient in iOS

This can be solved by some degree using Size Class. The idea of size class is that we have many sets of constraints, and based on the device traits, we enabled some of them. This is more convenient to do in Storyboard (although very hard to reason about), but if we're doing in code (my prefer way), then it is a lot of code. And a lot of code means a lot of bugs.

If you take a look at iOSRes, we see the ratio 16:9 (height:width)

  • iPhone SE (320 x 568): 1.775
  • iPhone 6 (375 x 667): 1.778
  • iPhone 6+ (414 x 736): 1.778

They mostly have the same ratio. So we can have a simple approach, that scale elements based on ratio. Given the fact that the designer usually designs for iPhone 6 size, we can make that a base.

In this approach, the content will scale up or down depending on its ratio. You may argue that the idea of bigger phone is to display more, not to show the same content bigger. You may be right, in that case you need to create different constraints and different UIs. But if you want simple solutions that work, this is one of them

This is the technique I used when doing Windows Phone development, but it applies to many platforms as well

Calculate the ratio

class Device {
  // Base width in point, use iPhone 6
  static let base: CGFloat = 375

  static var ratio: CGFloat {
    return UIScreen.main.bounds.width / base
  }
}

Extension to make it convenient

We can have a computed property called adjusted that adjusts the size based on the ratio

extension CGFloat {

  var adjusted: CGFloat {
    return self * Device.ratio
  }
}

extension Double {

  var adjusted: CGFloat {
    return CGFloat(self) * Device.ratio
  }
}

extension Int {

  var adjusted: CGFloat {
    return CGFloat(self) * Device.ratio
  }
}

Use the ratio

You can adjust as much as you want

label.font = UIFont.systemFont(ofSize: 23.adjusted)

phoneTextField.leftAnchor.constraint(equalTo: container.leftAnchor, constant: 30.adjusted),
phoneTextField.rightAnchor.constraint(equalTo: container.rightAnchor, constant: -30.adjusted),

imageView.widthAnchor.constraint(equalToConstant: 80.adjusted), imageView.heightAnchor.constraint(equalToConstant: 90.adjusted),

How to make a simple resolver in Swift

The Marvel world

Ant Man

We know Ant Man is Hank Pym

struct AntManSuit {
    let name: String
}

struct HankPym {
    let suit = AntManSuit(name: "Ant Man ID #101")

    func fight() {
        print("Fighting with the suit named " + suit.name)
    }
}

let hankPym = HankPym()
hankPym.fight()

Everytime HankPym is created, he always uses the Ant Man suit. This time he is so coupled to the role Ant Man

More suits

Well, he does not have to be too dependent on the Ant Man suit. We know Hank Pym is a genius scientist, he has more suits to use. Let's make it decoupled

Using Dependency Injection

protocol Suit {
    var name: String { get }
}

struct AntManSuit: Suit {
    let name: String
}

struct YellowJacketSuit: Suit {
    let name: String
}

struct HankPym {
    let suit: Suit

    func fight() {
        print("Fighting with the suit named " + suit.name)
    }
}

let suit = YellowJacketSuit(name: "Yellow Jacket ID #33")
let hankPym = HankPym(suit: suit)
hankPym.fight()

Now Hank Pym can be more flexible on which suit to use.

Dependency Injection

The technique we just saw is called Dependency Injection, in which Hank Pym does not need to create the Suit, it will be provided through constructor or property.

Dependency Inversion Principle

In the first example, Hank Pym is dependent on the concrete implementation of the Suit

In the second example, both Hank Pym and the suits are dependent on the Suit protocol. This way Hank Pym only knows about the Suit protocol, and future suits must be crafted to that it conforms to the Suit protocol

This way the dependency is inverted

High level modules should not depend upon low level modules. Both should depend upon abstractions.

What is the high level policy? It is the abstractions that underlie the application, the
truths that do not vary when the details are changed

Inversion of Control Container

You may ask yourself Why is Inversion of Control named that way?

Framework vs library

People said "the framework calls you but you call the library"

Command line vs GUI

See What is Inversion of Control?

For example, in an old school menu, you might have:

print "enter your name"
read name
print "enter your address"
read address
etc...
store in database

thereby controlling the flow of user interaction.

In a GUI program or some such, instead we say

when the user types in field a, store it in NAME
when the user types in field b, store it in ADDRESS
when the user clicks the save button, call StoreInDatabase

You how have a brief understanding of how IoC means

IoC container

In the 2nd example of the Suit protocol, you can see how there is a inversion of control. What if there is a container that contains all the Suit conformances?

Let's use my Resolver

let resolver = Resolver()
resolver.register {
    YellowJacketSuit(name: "YellowJacket ID #404") as Suit
}

let suit = try! resolver.resolve() as Suit
let hankPym = HankPym(suit: suit)

Quite helpful, right? :]

Features

Actually, IoC container helps you more than that.

  • Circular Dependency Injection
  • Auto Injection
  • Object Scope
  • ...

There are some IoC containers in Swift

Swinject

let container = Container()
container.register(AnimalType.self) { _ in Cat(name: "Mimi") }
container.register(PersonType.self) { r in
     PetOwner(pet: r.resolve(AnimalType.self)!)
}

Swinject requires explicit type declaration. It has SwinjectStoryboard, which helps configuring the dependency for your view controller

Dip

Dip leverages generic and encourage protocols

container.register { ServiceImp() as Service }
let service = try! container.resolve() as Service

You 'll learn a lot just by reading Dip source code, on how factory and factory type are stored and checked using generic

public func resolve<T, F>(tag tag: Tag? = nil, builder: F->T) throws -> T {
    let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag)
    let nilTagKey = tag.map { _ in DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: nil) }

    guard let definition = (self.definitions[key] ?? self.definitions[nilTagKey]) as? DefinitionOf<T, F> else {
      throw DipError.DefinitionNotFound(key)
    }

    let usingKey: DefinitionKey? = definition.scope == .ObjectGraph ? key : nil
    return _resolve(tag, key: usingKey, definition: definition, builder: builder)
  }

Build your own simple IoC container

You may have discovered, that the idea of all those framework is to use closure as factory method

let factory = {
    YellowJacketSuit(name: "YellowJacket ID #007") as Suit
}

let suit = factory()

All we have to do is to store these factories closure

Take a look at my gist SimpleResolver.swift

class SimpleResolver {
    var factories = [String: Any]()

    func factory<T>(factory: () -> T) {
        let key = String(T.self)
        factories[key] = factory
    }

    func resolve<T>() -> T {
        let key = String(T.self)
        if let factory = factories[key] as? () -> T {
            return factory()
        } else {
            fatalError("Registration not found")
        }
    }
}

let resolver = SimpleResolver()

resolver.factory {
    YellowJacketSuit(name: "YellowJacket IS #009") as Suit
}

let suit = resolver.resolve() as Suit
print(suit.name)

Reference

How to handle RefreshControl in iOS

The other day I was doing refresh control, and I saw this Swift Protocols with Default Implementations as UI Mixins

extension Refreshable where Self: UIViewController
{
    /// Install the refresh control on the table view
    func installRefreshControl()
    {
        let refreshControl = UIRefreshControl()
        refreshControl.tintColor = .primaryColor
        refreshControl.addTarget(self, action: #selector(handleRefresh(_:)), for: .valueChanged)
        self.refreshControl = refreshControl
        
        if #available(iOS 10.0, *)
        {
            tableView.refreshControl = refreshControl
        }
        else
        {
            tableView.backgroundView = refreshControl
        }
    }
}

Protocol extension is cool but somehow I'm not a fan of it. I always consider composition first, to extract the specific task to one entity that does that well. It looks like this

class RefreshHandler: NSObject {
  let refresh = PublishSubject<Void>()
  let refreshControl = UIRefreshControl()

  init(view: UIScrollView) {
    super.init()
    view.addSubview(refreshControl)
    refreshControl.addTarget(self, action: #selector(refreshControlDidRefresh(_: )), for: .valueChanged)
  }

  // MARK: - Action

  func refreshControlDidRefresh(_ control: UIRefreshControl) {
    refresh.onNext(())
  }

  func end() {
    refreshControl.endRefreshing()
  }
}

It is a bit Rx, we can use block if we like, but the idea is we can declare this RefreshHandler and use it everywhere we want

refreshHandler = RefreshHandler(view: scrollView)

refreshHandler.refresh
      .startWith(())
      .bindTo(viewModel.input.fetch)
      .addDisposableTo(bag)

How to deal with singleton in iOS

A single singleton

There are many classes that designed to be used as singleton, like UserDefaults.standard, FileManager.default, NotificationCenter.default or even our own classes like UserManager, Storage, ... Singleton is a design patter and has its own use case, sometimes we still need to use it. But if we are to use singleton, we should just use 1, and group all other singleton under this single singleton. Thanks to Vadym for showing this to me

Swift makes it extremely easy to make singleton, let name it App then we have a single point of control for all the singletons

struct App {
  static let model = AppModel()
  static let realmProvider = RealmProvider()
  static let networkingProvider = NetworkingProvider()
  static var navigator = Navigator()
  static let config = AppConfig()
  static let pushNotificationCenter = PushNotificationCenter()
  static let lifeCycle = LifeCycle()
}

These are use cases where a single instance is needed

AppModel

This is where we store model for an app, that can be

  • is onboarding shown
  • organization name
  • Session that encapsulates token, current profile

LifeCycle

This is where we listen to app life cycle, I use rx to make it easy, see #12

RealmProvider

I prefer Realm for storing and caching, usually 1 Realm is enough. This is where we return the a certain Realm instance

class RealmProvider {
  static func realm() -> Realm {
    let configuration = Realm.Configuration(schemaVersion: App.config.schemaVersion)
    return try! Realm(configuration: configuration)
  }
}

AppConfig

This is where we have configurations for staging and production environment, those can be client key, Firebase configuration, analytics keys, ...

Navigator

I use Compass to do central navigation, and there should be 1 Navigator that does the job

Inject a singleton

Sometime we rely on a singleton to do our job, to make dependencies clear and testing easier, we need to inject this singleton, and leverage Swift default parameter, thanks to John for showing this to me

Here is an example of a ViewModel that relies on networking

class ProfileViewModel {

  let networking: Networking<APIEndpoint>

  init(networking: Networking<APIEndpoint> = App.networking) {
    self.networking = networking

    networking.rxRequest(APIEndpoint.profile)
      .bindNext({ profile in
        print(profile)
      })
  }
}

How to do curry in Swift

Haskell is notorious for currying, and Swift has currying, too

I love ReactiveCocoa, RxSwift and I always take time to dig into it. The other day, I was practise making Signal based on this talk UIKonf 2015 - Jens Ravens: Functional Reactive Programming without Black Magic

Take a look at my repo Signal

filter

I was making a filter for a Signal. The idea of filter is that we should update signal if the Event is Next with right filtered value

Signal.swift

public func filter(f: T -> Bool) -> Signal<T>{
    let signal = Signal<T>()
    subscribe { result in
        switch(result) {
        case let .Success(value):
            if f(value) {
                signal.update(result)
            }
        case let .Error(error): signal.update(.Error(error))
        }
    }
    return signal
}

2 params

But having Event as another monad, I think it should be more encapsulated if that switching logic gets moved into the Event. Here the filter takes 2 params

Event.swift

func filter(f: T -> Bool, callback: (Event<T> -> Void)) {
        switch self {
        case let .Next(value) where f(value):
            callback(self)
        case .Failed:
            callback(self)
        default:
            break
    }
}

Signal.swift

public func filter(f: T -> Bool) -> Signal<T> {
    let signal = Signal<T>()

    subscribe { event in
        event.filter(f, callback: signal.update)
    }

    return signal
}

Currying

With currying, we can make filter a more abstract function, and defer the decision to pass the callback param. It is a little carried away but I find it helpful this way

Now filter accepts 1 param, and it returns a function that takes callback as its param

Event.swift

func filter(f: T -> Bool) -> ((Event<T> -> Void) -> Void) {
        return { g in
            switch self {
            case let .Next(value) where f(value):
                g(self)
            case .Failed:
                g(self)
            default:
                break
            }
        }
    }

Signal.swift

public func filter(f: T -> Bool) -> Signal<T> {
        let signal = Signal<T>()

        subscribe { event in
            event.filter(f)(signal.update)
        }

        return signal
    }

Curry syntax in Swift 2

Swift 2 supports curry syntax function

func sum(a: Int)(b: Int) -> Int {
    return a + b
}

let sumWith5 = sum(5)
let result = sumWith5(b: 10)

No more curry syntax in Swift 3

You may want to find out

Reference

How to debug Auto Layout

hasAmbiguousLayout

Returns whether the constraints impacting the layout of the view incompletely specify the location of the view.

exerciseAmbiguityInLayout

This method randomly changes the frame of a view with an ambiguous layout between its different valid values, causing the view to move in the interface. This makes it easy to visually identify what the valid frames are and may enable the developer to discern what constraints need to be added to the layout to fully specify a location for the view.

_autolayoutTrace

This returns a string describing the whole view tree which tells you when a view has an ambiguous layout.

NSLayoutConstraint identifier

The name that identifies the constraint.

UIViewAlertForUnsatisfiableConstraints

DETECTED_MISSING_CONSTRAINTS

https://forums.developer.apple.com/thread/63811

View Debugger search by address

Read more

How to group digits in Swift

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

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

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

How to run UI Test with system alert in iOS

Continue my post #45. When you work with features, like map view, you mostly need permissions, and in UITests you need to test for system alerts.

Add interruption monitor

This is the code. Note that you need to call app.tap() to interact with the app again, in order for interruption monitor to work

addUIInterruptionMonitor(withDescription: "Location permission", handler: { alert in
  alert.buttons["Allow"].tap()
  return true
})

app.tap()

Note that you don't always need to handle the returned value of addUIInterruptionMonitor

Only tap when needed

One problem with this approach is that when there is no system alert (you already touched to allow before), then app.tap() will tap on your main screen. In my app which uses map view, it will tap on some pins, which will present another screen, which is not correct.

Since app.alerts does not work, my 2nd attempt is to check for app.windows.count. Unfortunately, it always shows 5 windows whether alert is showing or not. I know 1 is for main window, 1 is for status bar, the other 3 windows I have no idea.

The 3rd attempt is to check that underlying elements (behind alert) can't be touched, which is to use isHittable. This property does not work, it always returns true

Check the content

This uses the assumption that we only tests for when user hits Allow button. So only if alert is answered with Allow, then we have permission to display our content. For my map view, I check that there are some pins on the map. See #45 on how to mock location and identify the pins

if app.otherElements.matching(identifier: "myPin").count == 0 {
  app.tap()
}

When there is no permission

So how can we test that user has denied your request? In my map view, if user does not allow location permission, I show a popup asking user to go to Settings and change it, otherwise, they can't interact with the map.

I don't know how to toggle location in Privacy in Settings, maybe XCUISiriService can help. But 1 thing we can do is to mock the application

Before you launch the app in UITests, add some arguments

app.launchArguments.append("--UITests-mockNoLocationPermission")

and in the app, we need to check for this arguments

func checkLocationPermission() {
  if CommandLine.arguments.contains("--UITests-mockNoLocationPermission") {
    showNoLocationPopupAndAskUserToEnableInSettings()
  }
}

That's it. In UITests, we can test whether that no location permission popup appears or not

How to make iOS Stretchy Header with Auto Layout

Stretchy header is cool. People are familiar with changing frames to achieve this, like Design Teardown: Stretchy Headers. But with Auto Layout, we can achieve this with much nicer declarative constraints

The demo project is StretchyHeader

demo

I use SnapKit to make it clear what constraints we need

scrollView

The scrollView should pin its 4 edges to the ViewController 's view

func setupScrollView() {
        scrollView = UIScrollView()
        scrollView.delegate = self

        view.addSubview(scrollView)
        scrollView.snp_makeConstraints { make in
            make.edges.equalTo(view)
        }
    }

scrollViewContentView

The scrollViewContentView must pin its 4 edges to the scrollView to help determine scrollView contentSize

The height of scrollViewContentView is determined by its subviews. The subviews inside must pin their top and bottom to the scrollViewContentView

func setupScrollViewContentView() {
        scrollViewContentView = UIView()

        scrollView.addSubview(scrollViewContentView)
        scrollViewContentView.snp_makeConstraints { make in
            make.edges.equalTo(scrollView)
            make.width.equalTo(view.snp_width)
        }
    }

header

The header must pin its top to the scrollView parent, which is the ViewController 's view

Read the title section, you 'll see that in order to make header stretchy, it must be pinned top and bottom

But if we scroll up, there will be a constraint conflict between these pinned top and bottom constraints

So we must declare headerTopConstraint priority as 999, and headerLessThanTopConstraint

func setupHeader() {
        header = UIImageView()
        header.image = UIImage(named: "onepiece")!

        scrollViewContentView.addSubview(header)
        header.snp_makeConstraints { make in
            // Pin header to scrollView 's parent, which is now ViewController 's view
            // When header is moved up, headerTopConstraint is not enough, so make its priority 999, and add another less than or equal constraint
            make.leading.trailing.equalTo(scrollViewContentView)
            self.headerTopConstraint =  make.top.equalTo(view.snp_top).priority(999).constraint
            self.headerLessThanTopConstraint = make.top.lessThanOrEqualTo(view.snp_top).constraint
        }
    }

title

The title must pin its top to the scrollViewContentView to help determine scrollViewContentView height

The title must also pin its top the header bottom in order to make header stretchy

func setupTitleLabel() {
        titleLabel = UILabel()
        titleLabel.numberOfLines = 0
        titleLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleTitle1)
        titleLabel.text = "One Piece"

        scrollViewContentView.addSubview(titleLabel)
        titleLabel.snp_makeConstraints { make in
            make.leading.equalTo(scrollViewContentView).offset(20)
            make.trailing.equalTo(scrollViewContentView).offset(-20)
            // Pin to the header to make it stretchy
            make.top.equalTo(header.snp_bottom).offset(20)
            // Pin to the content view to help determine scrollView contentSize
            make.top.equalTo(scrollViewContentView.snp_top).offset(headerHeight)
        }
    }

scrollViewDidScroll

The header is always pinned to the top, unless you adjust it, here in scrollViewDidScroll

Here I use Constraint, which is a class from SnapKit, but the idea is to change the constant of the NSLayoutConstraint

func scrollViewDidScroll(scrollView: UIScrollView) {
        guard let headerTopConstraint = headerTopConstraint,
            headerLessThanTopConstraint = headerLessThanTopConstraint
            else {
                return
        }

        let y = scrollView.contentOffset.y
        let offset = y > 0 ? -y : 0

        headerLessThanTopConstraint.updateOffset(offset)
        headerTopConstraint.updateOffset(offset)
    }

By the way, did you just learn the story of One Piece :]

Reference

How to run UI Test with Facebook login

Today I'm trying to run some UITest on my app, which uses Facebook login. And here are some of my notes on it.

Challenges

  • The challenges with Facebook is it uses Safari controller, we we deal mostly with web view for now. Starting from iOS 9+, Facebook decided to use safari instead of native facebook app to avoid app switching. You can read the detail here Building the Best Facebook Login Experience for People on iOS 9
  • It does not have wanted accessibilityIdentifier or accessibilityLabel
  • The webview content may change in the future 😸

Create a Facebook test user

Luckily, you don't have to create your own Facebook user to test. Facebook supports test users that you can manage permissions and friends, very handy

test user

When creating the test user, you have the option to select language. That will be the displayed language in Safari web view. I choose Norwegian 🇳🇴 for now

language

Click the login button and show Facebook login

Here we use the default FBSDKLoginButton

var showFacebookLoginFormButton: XCUIElement {
  return buttons["Continue with Facebook"]
}

And then tap it

app.showFacebookLoginFormButton.tap()

Check login status

When going to safari Facebook form, user may have already logged in or not. So we need to handle these 2 cases. When user has logged in, Facebook will say something like "you have already logged in" or the OK button.

The advice here is to put breakpoint and po app.staticTexts, po app.buttons to see which UI elements are at a certain point.

You can check for the static text, or simply just the OK button

var isAlreadyLoggedInSafari: Bool {
  return buttons["OK"].exists || staticTexts["Du har allerede godkjent Blue Sea."].exists
}

Wait and refresh

But Facebook form is a webview, so its content is a bit dynamic. And UITest seems to cache content for fast query, so before checking staticTexts, we need to wait and refresh the cache

app.clearCachedStaticTexts()

This is the wait function

extension XCTestCase {
  func wait(for duration: TimeInterval) {
    let waitExpectation = expectation(description: "Waiting")

    let when = DispatchTime.now() + duration
    DispatchQueue.main.asyncAfter(deadline: when) {
      waitExpectation.fulfill()
    }

    // We use a buffer here to avoid flakiness with Timer on CI
    waitForExpectations(timeout: duration + 0.5)
  }
}

Wait for element to appear

But a more solid approach would be to wait for element to appear. For Facebook login form, they should display a Facebook label after loading. So we should wait for this element

extension XCTestCase {
  /// Wait for element to appear
  func wait(for element: XCUIElement, timeout duration: TimeInterval) {
    let predicate = NSPredicate(format: "exists == true")
    let _ = expectation(for: predicate, evaluatedWith: element, handler: nil)

    // Here we don't need to call `waitExpectation.fulfill()`

    // We use a buffer here to avoid flakiness with Timer on CI
    waitForExpectations(timeout: duration + 0.5)
  }
}

And call this before you do any further inspection on elements in Facebook login form

 wait(for: app.staticTexts["Facebook"], timeout: 5)

If user is logged in

After login, my app shows the main controller with a map view inside. So a basic test would be to check the existence of that map

if app.isAlreadyLoggedInSafari {
  app.okButton.tap()

  handleLocationPermission()
  // Check for the map
  XCTAssertTrue(app.maps.element(boundBy: 0).exists)
}

Handle interruption

You know that when showing the map with location, Core Location will ask for permission. So we need to handle that interruption as well. You need to ensure to call it early before the alert happens

fileprivate func handleLocationPermission() {
  addUIInterruptionMonitor(withDescription: "Location permission", handler: { alert in
    alert.buttons.element(boundBy: 1).tap()
    return true
  })
}

There is another problem, this monitor won't be called. So the workaround is to call app.tap() again when the alert will happen. In my case, I call app.tap() when my map has been shown for 1,2 seconds, just to make sure app.tap() is called after alert is shown

For a more detailed guide, please read #48

If user is not logged in

In this case, we need to fill in email and password. You can take a look at the The full source code section below. When things don't work or po does not show you the elements you needed, it's probably because of caching or you need to wait until dynamic content finishes rendering.

You need to wait for element to appear

Tap on the text field

You may get Neither element nor any descendant has keyboard focus, here are the workaround

  • If you test on Simulator, make sure Simulator -> Hardware -> Keyboard -> Connect Hardware Keyboard is not checked
  • wait a bit after tap
app.emailTextField.tap()

Clear all the text

The idea is to move the caret to the end of the textField, then apply each delete key for each character, then type the next text

extension XCUIElement {
  func deleteAllText() {
    guard let string = value as? String else {
      return
    }

    let lowerRightCorner = coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.9))
    lowerRightCorner.tap()

    let deletes = string.characters.map({ _ in XCUIKeyboardKeyDelete }).joined(separator: "")
    typeText(deletes)
  }
}

Change language

For my case, I want to test in Norwegian, so we need to find the Norwegian option and tap on that. It is identified as static text by UI Test

var norwegianText: XCUIElement {
  return staticTexts["Norsk (bokmål)"]
}
wait(for: app.norwegianText, timeout: 1)
app.norwegianText.tap()

The email text field

Luckily, email text field is detected by UI Test as text field element, so we can query for that. This uses predicate

var emailTextField: XCUIElement {
  let predicate = NSPredicate(format: "placeholderValue == %@", "E-post eller mobil")
  return textFields.element(matching: predicate)
}

The password text field

UI Test can't seem to identify the password text field, so we need to search for it by coordinate

var passwordCoordinate: XCUICoordinate {
  let vector = CGVector(dx: 1, dy: 1.5)
  return emailTextField.coordinate(withNormalizedOffset: vector)
}

This is the document for func coordinate(withNormalizedOffset normalizedOffset: CGVector) -> XCUICoordinate

Creates and returns a new coordinate with a normalized offset.
The coordinate's screen point is computed by adding normalizedOffset multiplied by the size of the element’s frame to the origin of the element’s frame.

Then type the password

app.passwordCoordinate.tap()
app.typeText("My password")

We should not use app.passwordCoordinate.referencedElement because it will point to email text field ❗️ 😢

Run that test again

Go to Xcode -> Product -> Perform Actions -> Test Again to run the previous test again

again

The full source code

import XCTest

class LoginTests: XCTestCase {
  var app: XCUIApplication!

  func testLogin() {
    continueAfterFailure = false
    app = XCUIApplication()
    app.launch()

    passLogin()
  }
}

extension LoginTests {
  func passLogin() {
    // Tap login
    app.showFacebookLoginFormButton.tap()
    wait(for: app.staticTexts["Facebook"], timeout: 5) // This requires a high timeout
     
    // There may be location permission popup when showing map
    handleLocationPermission()    

    if app.isAlreadyLoggedInSafari {
      app.okButton.tap()

      // Show map
      let map = app.maps.element(boundBy: 0)
      wait(for: map, timeout: 2)
      XCTAssertTrue(map.exists)

      // Need to interact with the app for interruption monitor to work
      app.tap()
    } else {
      // Choose norsk
     wait(for: app.norwegianText, timeout: 1)
      app.norwegianText.tap()

      app.emailTextField.tap()
      app.emailTextField.deleteAllText()
      app.emailTextField.typeText("[email protected]")

      app.passwordCoordinate.tap()
      app.typeText("Bob Alageaiecghfb Sharpeman")

      // login
      app.facebookLoginButton.tap()

      // press OK
      app.okButton.tap()

      // Show map
      let map = app.maps.element(boundBy: 0)
      wait(for: map, timeout: 2)
      XCTAssertTrue(map.exists)

      // Need to interact with the app for interruption monitor to work
      app.tap()
    }
  }

  fileprivate func handleLocationPermission() {
    addUIInterruptionMonitor(withDescription: "Location permission", handler: { alert in
      alert.buttons.element(boundBy: 1).tap()
      return true
    })
  }
}

fileprivate extension XCUIApplication {
  var showFacebookLoginFormButton: XCUIElement {
    return buttons["Continue with Facebook"]
  }

  var isAlreadyLoggedInSafari: Bool {
    return buttons["OK"].exists || staticTexts["Du har allerede godkjent Blue Sea."].exists
  }

  var okButton: XCUIElement {
    return buttons["OK"]
  }

  var norwegianText: XCUIElement {
    return staticTexts["Norsk (bokmål)"]
  }

  var emailTextField: XCUIElement {
    let predicate = NSPredicate(format: "placeholderValue == %@", "E-post eller mobil")
    return textFields.element(matching: predicate)
  }

  var passwordCoordinate: XCUICoordinate {
    let vector = CGVector(dx: 1, dy: 1.5)
    return emailTextField.coordinate(withNormalizedOffset: vector)
  }

  var facebookLoginButton: XCUIElement {
    return buttons["Logg inn"]
  }
}

extension XCTestCase {
  func wait(for duration: TimeInterval) {
    let waitExpectation = expectation(description: "Waiting")

    let when = DispatchTime.now() + duration
    DispatchQueue.main.asyncAfter(deadline: when) {
      waitExpectation.fulfill()
    }

    // We use a buffer here to avoid flakiness with Timer on CI
    waitForExpectations(timeout: duration + 0.5)
  }

  /// Wait for element to appear
  func wait(for element: XCUIElement, timeout duration: TimeInterval) {
    let predicate = NSPredicate(format: "exists == true")
    let _ = expectation(for: predicate, evaluatedWith: element, handler: nil)

    // We use a buffer here to avoid flakiness with Timer on CI
    waitForExpectations(timeout: duration + 0.5)
  }
}

extension XCUIApplication {
  // Because of "Use cached accessibility hierarchy"
  func clearCachedStaticTexts() {
    let _ = staticTexts.count
  }

  func clearCachedTextFields() {
    let _ = textFields.count
  }

  func clearCachedTextViews() {
    let _ = textViews.count
  }
}

extension XCUIElement {
  func deleteAllText() {
    guard let string = value as? String else {
      return
    }

    let lowerRightCorner = coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.9))
    lowerRightCorner.tap()

    let deletes = string.characters.map({ _ in XCUIKeyboardKeyDelete }).joined(separator: "")
    typeText(deletes)
  }
}

Read more

I found these guides to cover many aspects of UITests, worth taking a look

Markdown editor

I like writing with markdown, it gives me comfortable experience with complete control over what I want to write.

I recommend vmd which renders exactly as GitHub. vmd is for rendering only, you need an editor to write, I use Sublime Text because it opens very fast

I also recommend using spectacle to easily split and organize windows

vmd

How to group extension methods in Swift

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

extension UIView {

  func shake() {

  }

  func fade() {

  }

  func centerIn(anotherView: UIView) {

  }
}

If you ‘re afraid of the naming conflict, you can prefix your methods, like

view.abc_shake()
view.abc_fade()
view.abc_centerIn(anotherView: containerView)

Or a better way, reverse it 💃 , like

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

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

How to execute an action only once in Swift

There is time we want to execute an action only once. We can surely introduce a flag, but it will be nicer if we abstract that out using composition. Then we have

class Once {

  var already: Bool = false

  func run(@noescape block: () -> Void) {
    guard !already else { 
      return 
    }

    block()
    already = true
  }
}

Usage

class ViewController: UIViewController {
  let once = Once()

  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    once.run {
      cameraMan.setup()
    }
  }
}

In the same way, we can check to run a closure when a value changes

final class WhenChange<T: Equatable> {
    private(set) var value: T

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

    func run(newValue: T, closure: (T) -> Void) {
        if newValue != value {
            value = newValue
            closure(value)
        }
    }
}

How to hack iOS apps

We need to care about security nowadays, here are some links I find useful to read more about this matter

Detecting languages and framework

iOS Security

Private frameworks

Hack macOS apps

Private frameworks

Hacking Apple

Hack Android apps

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.