readme.md 4.79 KB
Newer Older
Aral Balkan's avatar
Aral Balkan committed
1 2
# Delay

3
Delay is an easy-to-use micro-framework for delaying code execution in a cancellable manner. It uses Grand Central Dispatch and blocks.
Aral Balkan's avatar
Aral Balkan committed
4

5 6 7 8
## Installation

[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)

9
Run the `./install` script to install the dependencies for the demo apps.
10

11
See the [detailed installation instructions](#detailed-installation-instructions), below, for instructions on how to add Delay to your own apps.
12

13
## Usage
14

15
Run the `./dev` script and play with the iOS and OS X demo apps.
Aral Balkan's avatar
Aral Balkan committed
16

17
## Detailed usage instructions
Aral Balkan's avatar
Aral Balkan committed
18 19 20 21 22 23 24 25 26

Delay has three main use cases:

  * Delay execution of a block to the next stack frame (next frame of the runloop)
  * Delay execution by N seconds
  * Delay execution in a cancellable manner

### Execute on next stack frame

27 28 29 30 31 32
```swift
delay(0.0)
{
  // Do something.
}
```
Aral Balkan's avatar
Aral Balkan committed
33 34 35

### Execute after an arbitrary number of seconds

36 37 38 39 40 41
```swift
delay(42.0)
{
  // Do something.
}
```
Aral Balkan's avatar
Aral Balkan committed
42 43
### Cancel before execution

44 45 46 47 48 49 50
```swift
let cancellableBlock = delay(42.0)
{
  // Do something.
}
cancellableBlock.cancel()
```
Aral Balkan's avatar
Aral Balkan committed
51 52 53

### Idiom: throttle user input

54 55
```swift
var cancellableCommand:CancellableDelayedCommand?
Aral Balkan's avatar
Aral Balkan committed
56

57
// …
Aral Balkan's avatar
Aral Balkan committed
58

59 60 61 62 63 64 65 66
// Throttle expensive operation so it is performed at
// most every second, no matter how often a signal is received.
cancellableCommand = cancellableCommand?.reset() ?? delay(1.0)
{
  // Perform expensive operation:
  // …
}
```
Aral Balkan's avatar
Aral Balkan committed
67 68 69

For example, you can use this to implement auto-complete without flooding the lookup method on every keystroke.

70 71 72
```swift
var textDidChangeHandler:NotificationHandler?
var cancellableAutoCompleteCommand:CancellableDelayedCommand?
Aral Balkan's avatar
Aral Balkan committed
73

74
// …
Aral Balkan's avatar
Aral Balkan committed
75

76 77 78 79 80
override func viewWillAppear()
{
	textDidChangeHandler = handle(NSControlTextDidChangeNotification, from: myTextInput)
  {
    /* with */ notification in
81

82
    let text = self.myTextInput.stringValue
83

84 85 86 87 88 89 90 91
    // Throttle auto-complete lookups to every 1/3rd of a second.
    cancellableAutoCompleteCommand = cancellableAutoCompleteCommand?.reset() ?? delay(0.3)
    {
      // Perform expensive operation: look-up text for auto-complete
      // …
    }
  }
}
Aral Balkan's avatar
Aral Balkan committed
92

93 94 95 96 97 98
override func viewWillDisappear()
{
  cancellableAutoCompleteCommand.cancel()
  textDidChangeHandler.remove()
}
```
Aral Balkan's avatar
Aral Balkan committed
99 100 101 102 103 104 105

Read more about the library [in this blog post](https://ind.ie/labs/blog/delay) on [Ind.ie Labs](https://ind.ie/labs).

## On performance

Note that Delay is optimised for ease of authoring, beauty of interface and clarity of intent. I haven’t run into performance issues and hence haven’t felt the need to run any benchmarks at the moment but [Evgenii Rtishchev’s purely-block-based CancelBlocks](https://github.com/katleta3000/CancelBlocks/blob/master/CancelBlocks.swift), on which my solution is based, is a lighter alternative should you need it.

106 107
## Detailed installation instructions

108
### Carthage
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

1. Add the framework to your Carfile. e.g.,

	```git "git@source.ind.ie:project/delay.git" ~> 1.0```

2. [Follow the instructions on Carthage’s readme](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).

### Manual

#### iOS

This requires hacking the Xcode project file but it’s still the most elegant way of doing things. If you want a better way, [dupe this radar for seamless support of fat frameworks in Xcode](http://openradar.appspot.com/radar?id=4951631992979456).

1. Build the framework target.
2. In Xcode, add the framework to the Embed Binaries
3. Edit the `project.pbxproj` (in the <your-project>.xcodeproj folder) file and replace the line that reads something like:

	```A7E653511C2496F700988537 /* Delay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Delay.framework; path = "../../path/in/DerivedData/to/Delay.framework"; sourceTree = "<group>"; };```

	With:

	```A7E653511C2496F700988537 /* Delay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Delay.framework; path = "$(CONFIGURATION_BUILD_DIR)/Delay.framework"; sourceTree = "<group>"; };```

(In other words, you’re replacing the hardcoded path to the Delay.framework within your particular Derived Data folder with a generic one. This means that anyone else who checks out your project will get the correct framework.)

#### OS X

1. Just drag the framework into the Linked Frameworks and Binaries section of your project (under General).

(For an example of manually adding the framework, see the iOS and OS X demo apps that ship with this framework.)

Aral Balkan's avatar
Aral Balkan committed
140 141 142 143
## Credits

Delay based on the work of [Evgenii Rtishchev](https://github.com/katleta3000/CancelBlocks) and [Chris Brind](http://stackoverflow.com/questions/24034544/dispatch-after-gcd-in-swift/24318861#24318861) (with thanks to [Cezary Wojcik](http://stackoverflow.com/a/24034838/253485).)

144
Copyright © Aral Balkan.
145
Released with ♥ by [Ind.ie](https://ind.ie) under the MIT License.