├── .swift-version
├── Assets
├── Banner.png
└── Banner.psd
├── CONTRIBUTORS.md
├── Brisk.xcworkspace
└── contents.xcworkspacedata
├── Brisk.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ └── Brisk.xcscheme
└── project.pbxproj
├── .travis.yml
├── BriskTests
├── Info.plist
├── Spin.swift
└── BriskTests.swift
├── Brisk
├── Info.plist
├── BriskRaise.swift
├── Brisk.h
├── BriskGate.swift
├── BriskLock.swift
├── BriskAsync2Sync.swift
├── Swift2
│ └── BriskGCD_Swift2.swift
├── BriskDispatch.swift
└── BriskSync2Async.swift
├── CHANGELOG.md
├── LICENSE
├── Brisk.podspec
├── Package.swift
├── .gitignore
├── README_SWIFT2.md
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
--------------------------------------------------------------------------------
/Assets/Banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmfieldman/Brisk/HEAD/Assets/Banner.png
--------------------------------------------------------------------------------
/Assets/Banner.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmfieldman/Brisk/HEAD/Assets/Banner.psd
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | # Contributors
2 |
3 | * [Jason Fieldman](https://github.com/jmfieldman) - Maintainer
4 |
--------------------------------------------------------------------------------
/Brisk.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Brisk.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode8.3
3 | branches:
4 | only:
5 | - master
6 | env:
7 | - LC_CTYPE=en_US.UTF-8 LANG=en_US.UTF-8
8 | before_install:
9 | - SIMULATOR_ID=$(xcrun instruments -s | grep -o "iPhone 6S (10.3) \[.*\]" | grep -o "\[.*\]" | sed "s/^\[\(.*\)\]$/\1/")
10 | - gem install xcpretty -N
11 | script:
12 | - echo $SIMULATOR_ID
13 | - open -b com.apple.iphonesimulator --args -CurrentDeviceUDID $SIMULATOR_ID
14 | - set -o pipefail
15 | - xcodebuild -project Brisk.xcodeproj -scheme "Brisk" -sdk "iphonesimulator10.3"
16 | -destination "OS=10.3,name=iPhone 6S" ONLY_ACTIVE_ARCH=NO test | xcpretty -c
17 | - pod lib lint --quick
18 |
--------------------------------------------------------------------------------
/BriskTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Brisk/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Brisk Changelog
2 |
3 | ## 3.1.1 -- 3/31/17
4 |
5 | * Trying to fix cocoapods internal version tagging issue
6 |
7 | ## 3.1.0 -- 3/30/17
8 |
9 | * Updates for Xcode 8.3/Swift 3.1
10 |
11 | ## 3.0.2 -- 12/24/16
12 |
13 | * Added fatalerror calls when passing an optional function to the await operators (<<+) since those must guarantee to call their return function.
14 |
15 | ## 3.0.1 -- 10/13/16
16 |
17 | * Fixed podspec issue for Swift 3.0/cocoapods
18 |
19 | ## 3.0.0 -- 9/22/16
20 |
21 | * First release for Swift 3.0
22 | * Changed GCD component for LibDispatch updates
23 |
24 | ## 2.3.1 -- 9/11/16
25 |
26 | * First release for Swift 2.3
27 | * Removed OSSpinLock API [info](http://engineering.postmates.com/Spinlocks-Considered-Harmful-On-iOS/)
28 |
29 | ## 2.2.2 -- 8/16/16
30 |
31 | * Added operators for optional functions (```?+>>``` and ```?~>>```)
32 |
33 | ## 2.2.1 -- 8/14/16
34 |
35 | * Initial Release for Swift 2.2
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Jason Fieldman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Brisk.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "Brisk"
4 | s.version = "3.1.1"
5 | s.summary = "Concise concurrency manipulation for Swift"
6 |
7 | s.description = <<-DESC
8 | Concise concurrency manipulation for Swift: completionHandler+>>()
9 | DESC
10 |
11 | s.homepage = "https://github.com/jmfieldman/Brisk"
12 | s.license = { :type => "MIT", :file => "LICENSE" }
13 | s.author = { "Jason Fieldman" => "jason@fieldman.org" }
14 | s.social_media_url = 'http://fieldman.org'
15 |
16 | s.ios.deployment_target = "8.0"
17 | s.osx.deployment_target = "10.10"
18 | s.tvos.deployment_target = "9.0"
19 | s.watchos.deployment_target = "2.0"
20 |
21 | s.source = { :git => "https://github.com/jmfieldman/Brisk.git", :tag => "#{s.version}" }
22 | s.source_files = "Brisk/*.swift"
23 |
24 | s.requires_arc = true
25 |
26 | s.default_subspec = 'Core'
27 |
28 | s.subspec 'Core' do |ss|
29 | ss.source_files = "Brisk/*.swift"
30 | end
31 |
32 | s.subspec 'Swift2' do |ss|
33 | ss.source_files = "Brisk/Swift2/*.swift"
34 | ss.dependency 'Brisk/Core'
35 | end
36 |
37 | end
38 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Package.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | import PackageDescription
26 |
27 | let package = Package(
28 | name: "Brisk"
29 | )
--------------------------------------------------------------------------------
/Brisk/BriskRaise.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskRaise.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | internal func brisk_raise(_ reason: String) -> Never {
29 | NSException(name: NSExceptionName(rawValue: "Brisk Exception"), reason: reason, userInfo: nil).raise()
30 | fatalError("Brisk usage exceptions are fatal errors.")
31 | }
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/screenshots
64 |
--------------------------------------------------------------------------------
/Brisk/Brisk.h:
--------------------------------------------------------------------------------
1 | //
2 | // Brisk.h
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | #import
27 |
28 | //! Project version number for Brisk.
29 | FOUNDATION_EXPORT double BriskVersionNumber;
30 |
31 | //! Project version string for Brisk.
32 | FOUNDATION_EXPORT const unsigned char BriskVersionString[];
33 |
34 | // In this header, you should import all the public headers of your framework using statements like #import
35 |
36 |
37 |
--------------------------------------------------------------------------------
/BriskTests/Spin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Spin.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | protocol Spin : AnyObject {
29 | func start()
30 | func done()
31 | func wait()
32 | }
33 |
34 |
35 | internal class MainSpin : Spin {
36 | var finished: Bool = false
37 |
38 | func start() {
39 | self.finished = false
40 | }
41 |
42 | func done() {
43 | self.finished = true
44 | }
45 |
46 | func wait() {
47 | while !finished {
48 | RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: Date(timeIntervalSinceNow: 0.1))
49 | }
50 | }
51 | }
52 |
53 | internal class AsyncSpin : Spin {
54 | let group = DispatchGroup()
55 |
56 |
57 | func start() {
58 | group.enter()
59 | }
60 |
61 | func done() {
62 | group.leave()
63 | }
64 |
65 | func wait() {
66 | _ = group.wait(timeout: DispatchTime.distantFuture)
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/Brisk/BriskGate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskGate.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 |
29 | // BriskGate is an intelligent semaphore mechanism that can
30 | // perform waits from the main thread without freezing the
31 | // application (though it does so inefficiently).
32 |
33 | internal class BriskGate {
34 |
35 | var isMain: Bool
36 | var group: DispatchGroup? = nil
37 | var finished: Bool = false
38 |
39 | init() {
40 | isMain = Thread.current.isMainThread
41 | if !isMain {
42 | group = DispatchGroup()
43 | group!.enter()
44 | }
45 | }
46 |
47 | func signal() {
48 | finished = true
49 | if !isMain {
50 | group!.leave()
51 | }
52 | }
53 |
54 | func wait() {
55 | if isMain {
56 | while !finished {
57 | RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: Date(timeIntervalSinceNow: 0.1))
58 | }
59 | } else {
60 | _ = group!.wait(timeout: DispatchTime.distantFuture)
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/Brisk/BriskLock.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskLock.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 |
29 |
30 |
31 | // MARK: - Concurrency Synchronization
32 |
33 |
34 | /// Perform a block synchronized on a NSLocking object.
35 | ///
36 | /// - parameter lock: The NSLocking object to use.
37 | /// - parameter block: The block to perform.
38 | @inline(__always) public func synchronized(_ lock: NSLocking, block: () -> ()) {
39 | lock.lock()
40 | block()
41 | lock.unlock()
42 | }
43 |
44 |
45 | /// Perform a block synchronized on a NSLocking object. The block returns a value.
46 | ///
47 | /// - parameter lock: The NSLocking object to use.
48 | /// - parameter block: The block to perform.
49 | @inline(__always) public func synchronized(_ lock: NSLocking, block: () -> T) -> T {
50 | lock.lock()
51 | let r = block()
52 | lock.unlock()
53 | return r
54 | }
55 |
56 |
57 | /// Perform a block synchronized on a NSLocking object. The block returns an optional value.
58 | ///
59 | /// - parameter lock: The NSLocking object to use.
60 | /// - parameter block: The block to perform.
61 | @inline(__always) public func synchronized(_ lock: NSLocking, block: () -> T?) -> T? {
62 | lock.lock()
63 | let r = block()
64 | lock.unlock()
65 | return r
66 | }
67 |
68 |
69 | // For the universal synchronization
70 | private var universalLock: NSRecursiveLock = NSRecursiveLock()
71 |
72 |
73 | /// Perform a block synchronized on the global static spin lock.
74 | ///
75 | /// - parameter block: The block to perform.
76 | public func synchronized(_ block: () -> ()) {
77 | universalLock.lock()
78 | block()
79 | universalLock.unlock()
80 | }
81 |
82 |
83 | /// Perform a block synchronized on the global static spin lock. The block returns a value.
84 | ///
85 | /// - parameter block: The block to perform.
86 | public func synchronized(_ block: () -> T) -> T {
87 | universalLock.lock()
88 | let r = block()
89 | universalLock.unlock()
90 | return r
91 | }
92 |
93 |
94 | /// Perform a block synchronized on the global static spin lock. The block returns an optional value.
95 | ///
96 | /// - parameter block: The block to perform.
97 | public func synchronized(_ block: () -> T?) -> T? {
98 | universalLock.lock()
99 | let r = block()
100 | universalLock.unlock()
101 | return r
102 | }
103 |
104 |
105 |
--------------------------------------------------------------------------------
/Brisk.xcodeproj/xcshareddata/xcschemes/Brisk.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/Brisk/BriskAsync2Sync.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskAsync2Sync.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | // let .. = <<-{ func(i, handler: $0) } call func on current queue
29 | // let .. = <<~{ func(i, handler: $0) } call func on bg queue
30 | // let .. = <<+{ func(i, handler: $0) } call func on main queue
31 | // let .. = <<~myQueue ~~~ { func(i, handler: $0) } call func on specified queue
32 |
33 | prefix operator <<-
34 | prefix operator <<~
35 | prefix operator <<+
36 |
37 | /* -- old precendence = 95 -- */
38 | precedencegroup QueueRedirectionPrecendence {
39 | higherThan: AssignmentPrecedence
40 | lowerThan: TernaryPrecedence
41 | }
42 |
43 | infix operator ~~~ : QueueRedirectionPrecendence
44 |
45 |
46 | /// Returns the queue this prefix is applied to. This is used to prettify the
47 | /// syntax:
48 | ///
49 | /// - e.g.: let x = <<~myQueue ~~~ { func(i, handler: $0) }
50 | @inline(__always) public prefix func <<~(q: DispatchQueue) -> DispatchQueue {
51 | return q
52 | }
53 |
54 | /// Executes the attached operation synchronously on the current queue
55 | /// and waits for it to complete. Returns the result of the callback handler that
56 | /// $0 was attached to.
57 | ///
58 | /// - e.g.: ```let x = <<-{ func(i, callback: $0)``` }
59 | public prefix func <<-(operation: (_ callbackHandler: @escaping (_ param: O) -> ()) -> ()) -> O {
60 |
61 | // Our gating mechanism
62 | let gate = BriskGate()
63 |
64 | // This value will eventually hold the response from the async function
65 | var handledResponse: O?
66 |
67 | let theHandler: (_ p: O) -> () = { responseFromCallback in
68 | handledResponse = responseFromCallback
69 | gate.signal()
70 | }
71 |
72 | operation(theHandler)
73 | gate.wait()
74 |
75 | // It's ok to use ! -- theoretically we are garanteed that handledResponse
76 | // has been set by this point (inside theHandler)
77 | return handledResponse!
78 | }
79 |
80 | /// This protects against optional functions being placed inside a sync-to-async block.
81 | public prefix func <<-(operation: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> Void?) -> O {
82 | fatalError("You cannot put an optional call inside of a brisk sync-to-async block, since it must be guaranteed to call and return.")
83 | }
84 |
85 |
86 | /// Using a generic handler for the non-noescape versions
87 | @inline(__always) private func processAsync2Sync(_ operation: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> (),
88 | queue: DispatchQueue) -> O {
89 |
90 | // Our gating mechanism
91 | let gate = BriskGate()
92 |
93 | // This value will eventually hold the response from the async function
94 | var handledResponse: O?
95 |
96 | let theHandler: (_ p: O) -> () = { responseFromCallback in
97 | handledResponse = responseFromCallback
98 | gate.signal()
99 | }
100 |
101 | queue.async {
102 | operation(theHandler)
103 | }
104 |
105 | gate.wait()
106 |
107 | // It's ok to use ! -- theoretically we are garanteed that handledResponse
108 | // has been set by this point (inside theHandler)
109 | return handledResponse!
110 | }
111 |
112 |
113 | /// Executes the attached operation on the general concurrent background queue
114 | /// and waits for it to complete. Returns the result of the callback handler that
115 | /// $0 was attached to.
116 | ///
117 | /// - e.g.: ```let x = <<~{ func(i, callback: $0)``` }
118 | public prefix func <<~(operation: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> ()) -> O {
119 | return processAsync2Sync(operation, queue: backgroundQueue)
120 | }
121 |
122 | /// This protects against optional functions being placed inside a sync-to-async block.
123 | public prefix func <<~(operation: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> Void?) -> O {
124 | fatalError("You cannot put an optional call inside of a brisk sync-to-async block, since it must be guaranteed to call and return.")
125 | }
126 |
127 | /// Executes the attached operation on the main queue
128 | /// and waits for it to complete. Returns the result of the callback handler that
129 | /// $0 was attached to.
130 | ///
131 | /// - e.g.: ```let x = <<+{ func(i, callback: $0)``` }
132 | public prefix func <<+(operation: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> ()) -> O {
133 | return processAsync2Sync(operation, queue: mainQueue)
134 | }
135 |
136 | /// This protects against optional functions being placed inside a sync-to-async block.
137 | public prefix func <<+(operation: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> Void?) -> O {
138 | fatalError("You cannot put an optional call inside of a brisk sync-to-async block, since it must be guaranteed to call and return.")
139 | }
140 |
141 |
142 | /// Executes the attached operation on the supplied queue from the left side
143 | /// and waits for it to complete. Returns the result of the callback handler that
144 | /// $0 was attached to.
145 | ///
146 | /// - e.g.: ```let x = <<~myQueue ~~~ { func(i, callback: $0)``` }
147 | public func ~~~(lhs: DispatchQueue, rhs: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> ()) -> O {
148 | return processAsync2Sync(rhs, queue: lhs)
149 | }
150 |
151 | /// This protects against optional functions being placed inside a sync-to-async block.
152 | public func ~~~(lhs: DispatchQueue, rhs: @escaping (_ callbackHandler: @escaping (_ param: O) -> ()) -> Void?) -> O {
153 | fatalError("You cannot put an optional call inside of a brisk sync-to-async block, since it must be guaranteed to call and return.")
154 | }
155 |
156 |
157 |
--------------------------------------------------------------------------------
/Brisk/Swift2/BriskGCD_Swift2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskGCD.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 |
29 |
30 |
31 | /// The leeway granted for inexact timers.
32 | private let kInexactTimerLeeway = UInt64(0.01 * Double(NSEC_PER_SEC))
33 |
34 |
35 | // MARK: - Dispatch Helpers
36 |
37 |
38 | /// Execute a block asynchronously on the main dispatch queue
39 | ///
40 | /// - parameter block: The block to execute.
41 | public func dispatch_main_async(_ block: @escaping () -> ()) {
42 | mainQueue.async(execute: block)
43 | }
44 |
45 |
46 | /// Execute a block asynchronously on the generic concurrent background queue.
47 | ///
48 | /// - parameter block: The block to execute.
49 | public func dispatch_bg_async(_ block: @escaping () -> ()) {
50 | backgroundQueue.async(execute: block)
51 | }
52 |
53 |
54 | // Data Structures for dispatch_async
55 | private var queueForId: [String : DispatchQueue] = [:]
56 | private var queueLock: NSLock = NSLock()
57 |
58 | /// Executes a block on an ad-hoc named serial dispatch queue. If a queue was already created
59 | /// with the provided name, it is reused. This allows you to dispatch code on serial queues
60 | /// whose uniqueness is identified by a string, rather than a pre-allocated queue instance.
61 | /// - note: This is a more a convenience feature than a recommended practice. It is generally
62 | /// safer from a coding perspective to use a pre-instantiated queue variable.
63 | public func dispatch_async(_ queueName: String, block: @escaping () -> ()) {
64 |
65 | if let queue: DispatchQueue = synchronized(queueLock, block: { return queueForId[queueName] }) {
66 | queue.async(execute: block)
67 | return
68 | }
69 |
70 | synchronized(queueLock) {
71 | let queue = DispatchQueue(label: queueName, attributes: [])
72 | queueForId[queueName] = queue
73 | queue.async(execute: block)
74 | }
75 | }
76 |
77 |
78 | /// Execute a block synchronously on the main dispatch queue. If called
79 | /// from the main queue, the block is executed immediately.
80 | ///
81 | /// - parameter block: The block to execute.
82 | public func dispatch_main_sync(_ block: () -> ()) {
83 |
84 | if Thread.current.isMainThread {
85 | block()
86 | } else {
87 | mainQueue.sync(execute: block)
88 | }
89 | }
90 |
91 |
92 | /// Dispatch a block asynchronously on a dispatch queue after a certain time interval.
93 | /// The dispatch timer will use the standard leeway (non-exact).
94 | ///
95 | /// - parameter after: The time to wait before running the block asynchronously.
96 | /// - parameter block: The block to execute.
97 | @inline(__always) public func dispatch_after(_ seconds: TimeInterval,
98 | _ onQueue: DispatchQueue,
99 | _ block: @escaping () -> ()) {
100 |
101 | onQueue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: block)
102 | }
103 |
104 |
105 | /// Dispatch a block asynchronously on a dispatch queue after a certain time interval.
106 | /// The dispatch timer will use 0 leeway to make the timing as exact as possible. This is used
107 | /// mainly for animation or other activities that require exact timing.
108 | ///
109 | /// - parameter after: The time to wait before running the block asynchronously.
110 | /// - parameter block: The block to execute.
111 | @inline(__always) public func dispatch_after_exactly(_ seconds: TimeInterval,
112 | _ onQueue: DispatchQueue,
113 | _ block: @escaping (() -> ())) {
114 |
115 | let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: onQueue)
116 | timer.setEventHandler {
117 | block()
118 | }
119 |
120 | timer.scheduleOneshot(deadline: DispatchTime.now() + seconds, leeway: DispatchTimeInterval.microseconds(0))
121 | timer.resume()
122 | }
123 |
124 |
125 | /// Dispatch a block every interval on the given queue. You stop the timer by calling
126 | /// dispatch_source_cancel on the returned timer.
127 | ///
128 | /// - parameter interval: The interval to call the block.
129 | /// - parameter onQueue: The queue to call the block on.
130 | /// - parameter block: The block to execute.
131 | ///
132 | /// - returns: The dispatch_source_t that represents the timer.
133 | /// You must eventually cancel this with dispatch_source_cancel()
134 | @discardableResult @inline(__always) public func dispatch_every(_ interval: TimeInterval,
135 | _ onQueue: DispatchQueue,
136 | _ block: @escaping ((DispatchSourceTimer) -> ())) -> DispatchSourceTimer {
137 |
138 | let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: onQueue)
139 | timer.setEventHandler {
140 | block(timer)
141 | }
142 |
143 | timer.scheduleRepeating(deadline: DispatchTime.now() + interval, interval: interval)
144 | timer.resume()
145 | return timer
146 | }
147 |
148 |
149 | /// Dispatch a block every interval on the given queue. You stop the timer by calling
150 | /// dispatch_source_cancel on the returned timer.
151 | ///
152 | /// This function uses the most exact timing possible the timer.
153 | ///
154 | /// - parameter interval: The interval to call the block.
155 | /// - parameter onQueue: The queue to call the block on.
156 | /// - parameter block: The block to execute.
157 | ///
158 | /// - returns: The dispatch_source_t that represents the timer.
159 | /// You must eventually cancel this with dispatch_source_cancel()
160 | @discardableResult @inline(__always) public func dispatch_every_exact(_ interval: TimeInterval,
161 | _ onQueue: DispatchQueue,
162 | _ block: @escaping ((DispatchSourceTimer) -> ())) -> DispatchSourceTimer {
163 |
164 | let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: onQueue)
165 | timer.setEventHandler {
166 | block(timer as! DispatchSource)
167 | }
168 |
169 | timer.scheduleRepeating(deadline: DispatchTime.now() + interval, interval: interval, leeway: DispatchTimeInterval.microseconds(0))
170 | timer.resume()
171 | return timer as! DispatchSource
172 | }
173 |
174 |
175 | /// Dispatch a block asynchronously on the main dispatch queue after a certain time interval.
176 | /// The dispatch timer will use the standard leeway (non-exact).
177 | ///
178 | /// - parameter after: The time to wait before running the block asynchronously.
179 | /// - parameter block: The block to execute.
180 | public func dispatch_main_after(_ seconds: TimeInterval,
181 | _ block: @escaping () -> ()) {
182 |
183 | dispatch_after(seconds, mainQueue, block)
184 | }
185 |
186 |
187 | /// Dispatch a block asynchronously on the main dispatch queue after a certain time interval.
188 | /// The dispatch timer will use 0 leeway to make the timing as exact as possible. This is used
189 | /// mainly for animation or other activities that require exact timing.
190 | ///
191 | /// - parameter after: The time to wait before running the block asynchronously.
192 | /// - parameter block: The block to execute.
193 | public func dispatch_main_after_exactly(_ seconds: TimeInterval,
194 | _ block: @escaping (() -> ())) {
195 |
196 | dispatch_after_exactly(seconds, mainQueue, block)
197 | }
198 |
199 |
200 | /// Dispatch a block every interval on the main queue. You stop the timer by calling
201 | /// dispatch_source_cancel on the returned timer.
202 | ///
203 | /// - parameter interval: The interval to call the block.
204 | /// - parameter block: The block to execute.
205 | ///
206 | /// - returns: The dispatch_source_t that represents the timer.
207 | /// You must eventually cancel this with dispatch_source_cancel()
208 | @discardableResult @inline(__always) public func dispatch_main_every(_ interval: TimeInterval,
209 | _ block: @escaping ((DispatchSourceTimer) -> ())) -> DispatchSourceTimer {
210 |
211 | return dispatch_every(interval, DispatchQueue.main, block)
212 | }
213 |
214 |
215 | /// Dispatch a block every interval on the main queue. You stop the timer by calling
216 | /// dispatch_source_cancel on the returned timer.
217 | ///
218 | /// This function uses the most exact timing possible on the timer.
219 | ///
220 | /// - parameter interval: The interval to call the block.
221 | /// - parameter block: The block to execute.
222 | ///
223 | /// - returns: The dispatch_source_t that represents the timer.
224 | /// You must eventually cancel this with dispatch_source_cancel()
225 | @discardableResult @inline(__always) public func dispatch_main_every_exact(_ interval: TimeInterval,
226 | _ block: @escaping ((DispatchSourceTimer) -> ())) -> DispatchSourceTimer {
227 |
228 | return dispatch_every_exact(interval, DispatchQueue.main, block)
229 | }
230 |
231 |
232 |
233 | // Data Structures for dispatch_once_after
234 | private var operationTimerForId: [String : DispatchSourceTimer] = [:]
235 | private var operationTimerLock: NSLock = NSLock()
236 |
237 | /// Queue up an action to take place on an queue in the future, but make sure it only triggers once.
238 | /// This allows you to queue up the same operation several times and not worry about
239 | /// it being called multiple times later.
240 | public func dispatch_once_after(_ after: TimeInterval,
241 | operationId: String,
242 | onQueue queue: DispatchQueue,
243 | block: @escaping () -> ()) {
244 |
245 | // Check if we already have a timer source for this operation ID
246 | if let existingTimer: DispatchSourceTimer = synchronized(operationTimerLock, block: { return operationTimerForId[operationId] }) {
247 | existingTimer.scheduleOneshot(deadline: DispatchTime.now() + after)
248 | return
249 | }
250 |
251 | // Timer doesn't exist, we have to make one!
252 | let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: queue)
253 | timer.setEventHandler {
254 |
255 | // one shot timer -- remove from dictionary
256 | synchronized(operationTimerLock) {
257 | operationTimerForId.removeValue(forKey: operationId)
258 | timer.cancel()
259 | }
260 | block()
261 | }
262 |
263 | // Set it!
264 | synchronized(operationTimerLock) { operationTimerForId[operationId] = timer }
265 | timer.scheduleOneshot(deadline: DispatchTime.now() + after)
266 | timer.resume()
267 | }
268 |
269 |
270 | /// Queue up an action to take place on the main queue in the future, but make sure it only triggers once.
271 | /// This allows you to queue up the same operation several times and not worry about
272 | /// it being called multiple times later.
273 | public func dispatch_main_once_after(_ after: TimeInterval,
274 | operationId: String,
275 | block: @escaping () -> ()) {
276 |
277 | dispatch_once_after(after, operationId: operationId, onQueue: mainQueue, block: block)
278 | }
279 |
280 | /// This is a helper to take advantage of multiple cores when performing an activity in parallel on
281 | /// multiple values in an array.
282 | ///
283 | /// - parameter elements: An array of elements to process
284 | /// - parameter queue: The dispatch queue to process on (should be concurrent)
285 | /// - parameter block: The block to process for each element.
286 | public func dispatch_each(_ elements: [T],
287 | queue: DispatchQueue,
288 | block: (T) -> ()) {
289 |
290 | DispatchQueue.concurrentPerform(iterations: elements.count) { i in
291 | block(elements[i])
292 | }
293 | }
294 |
295 |
296 |
--------------------------------------------------------------------------------
/README_SWIFT2.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | > This is the README for the Swift 2.x Brisk Library
4 |
5 | Swift support for blocks and asynchronous code is powerful, but can lead to a maze of indented logic that
6 | quickly becomes unreadable and error-prone.
7 |
8 | Brisk offers two distinct but complimentary functions:
9 |
10 | 1. Extends the standard GCD library with several functions that help make standard usage a bit more concise.
11 | 2. Provides shorthand operators for swiveling the concurrency of your functions.
12 |
13 | These might be best explained by code example.
14 |
15 | ### Quick Look: Extending the GCD Library for Simplicity ###
16 |
17 | Consider the existing methods required from the standard GCD library:
18 |
19 | ```swift
20 | // Dispatch a block to the main queue
21 | dispatch_async(dispatch_get_main_queue()) {
22 | // ...
23 | }
24 |
25 | // Dispatch a block to the main queue after 2.0 seconds
26 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))),
27 | dispatch_get_main_queue()) {
28 | // ...
29 | }
30 | ```
31 |
32 | Compared to their Brisk equivalents:
33 |
34 | ```swift
35 | // Dispatch a block to the main queue
36 | dispatch_main_async {
37 | // ...
38 | }
39 |
40 | // Dispatch a block to the main queue after 2.0 seconds
41 | dispatch_main_after(2.0) {
42 | // ...
43 | }
44 | ```
45 |
46 | Brisk offers this type of simplification for many standard GCD use cases.
47 |
48 | ### Quick Look: Concurrency Swiveling ###
49 |
50 | Consider the following hypothetical asynchronous API (using Swift 2.2 function syntax):
51 |
52 | ```swift
53 | // API we're given:
54 | func findClosestPokemonWithin(within: Double,
55 | completionHandler: (pokemon: Pokemon?, error: NSError?) -> Void)
56 |
57 | func countPokeballs(completionHandler: (number: Int?, error: NSError?) -> Void)
58 |
59 | func throwPokeballAtPokemon(pokemon: Pokemon,
60 | completionHandler: (success: Bool, error: NSError?) -> Void)
61 | ```
62 |
63 | Let's assume that all of the completion handlers are called on the main thread. We want to
64 | make this utility function:
65 |
66 | ```swift
67 | // Utility we want:
68 | func throwAtClosestPokemonWithin(within: Double,
69 | completionHandler: (success: Bool, pokemon: Pokemon?, error: NSError?) -> Void)
70 | ```
71 |
72 | This function represents a common occurrence of chaining asynchronous functions into a helper utility for a single use case.
73 | Using only the standard GCD library, your function might look like this:
74 |
75 | ```swift
76 | // The old way...
77 | func throwAtClosestPokemonWithin(within: Double,
78 | completionHandler: (success: Bool, pokemon: Pokemon?, error: NSError?) -> Void) {
79 | // Step 1
80 | findClosestPokemonWithin(within) { pokemon, error in
81 | guard let p = pokemon where error == nil else {
82 | dispatch_main_async {
83 | completionHandler(success: false, pokemon: nil, error: error)
84 | }
85 | return
86 | }
87 |
88 | // Step 2
89 | countPokeballs { number, error in
90 | guard let n = number where error == nil else {
91 | dispatch_main_async {
92 | completionHandler(success: false, pokemon: nil, error: error)
93 | }
94 | return
95 | }
96 |
97 | // Step 3
98 | throwPokeballAtPokemon(p) { success, error in
99 | dispatch_main_async {
100 | completionHandler(success: success, error: error)
101 | }
102 | }
103 | }
104 | }
105 | }
106 | ```
107 |
108 | With Brisk:
109 |
110 | ```swift
111 | // The new way...
112 | func throwAtClosestPokemonWithin(within: Double,
113 | completionHandler: (success: Bool, pokemon: Pokemon?, error: NSError?) -> Void) {
114 | dispatch_bg_async {
115 |
116 | // Step 1
117 | let (pokemon, error) = <<+{ findClosestPokemonWithin(within, completionHandler: $0) }
118 | guard let p = pokemon where error == nil else {
119 | return completionHandler +>> (success: false, error: error)
120 | }
121 |
122 | // Step 2
123 | let (number, error2) = <<+{ countPokeballs($0) }
124 | guard let n = number where error2 == nil else {
125 | return completionHandler +>> (success: false, error: error2)
126 | }
127 |
128 | // Step 3
129 | let (success, error3) = <<+{ throwPokeballAtPokemon(p, completionHandler: $0) }
130 | completionHandler +>> (success: success, error: error3)
131 | }
132 | }
133 | ```
134 |
135 | The main advantage with Brisk is that the asynchronous functions can be coded using a seemingly-synchronous flow.
136 | The asynchronous nature of the methods is hidden behind the custom operators. *Unlike PromiseKit, all return values
137 | remain in scope as well*.
138 |
139 | ## Detailed GCD Additions ##
140 |
141 | The following code examples show the GCD additions provided by Brisk. These are
142 | fairly self-documenting. More information about each method can be found in its comment section.
143 |
144 | ```swift
145 | dispatch_main_async {
146 | // Block runs on the main queue
147 | }
148 |
149 | dispatch_main_sync {
150 | // Block runs on the main queue; this function does not return until
151 | // the block completes.
152 | }
153 |
154 | dispatch_bg_async {
155 | // Block runs on the global concurrent background queue
156 | }
157 |
158 | dispatch_async("myNewQueue") {
159 | // Block runs on a brisk-created serial queue with the specified string ID.
160 | // Calling this function multiple times with the same string will reuse the
161 | // named queue. Useful for dynamic throw-away serial queues.
162 | }
163 |
164 | dispatch_main_after(2.0) {
165 | dispatch_after(2.0, myQueue) {
166 | // Block is called on specified queue after specified number of seconds using
167 | // a loose leeway (+/- 0.1 seconds).
168 | }
169 |
170 | dispatch_main_after_exactly(2.0) {
171 | dispatch_after_exactly(2.0, myQueue) {
172 | // Block is called on specified queue after specified number of seconds using
173 | // as tight a timer leeway as possible. Useful for animation timing but
174 | // uses more battery power.
175 | }
176 |
177 | dispatch_main_every(2.0) { timer in
178 | dispatch_every(2.0, myQueue) { timer in
179 | dispatch_main_every_exact(2.0) { timer in
180 | dispatch_every_exact(2.0, myQueue) { timer in
181 | // Block is run on specified thread every N seconds.
182 | // Stop the timer with:
183 | dispatch_source_cancel(timer)
184 | }
185 |
186 | dispatch_main_once_after(2.0, "myOperationId") {
187 | dispatch_once_after(2.0, myQueue, "myOperationId") {
188 | // Block runs after specified time on specified queue. The block is
189 | // only executed ONCE -- repeat calls to this function with the same
190 | // operation ID will reset its internal timer instead of calling the
191 | // block again. Useful for calling a completion block after several
192 | // disparate asynchronous methods (e.g. saving the database to disk
193 | // after downloading multiple records on separate threads.)
194 | }
195 |
196 | dispatch_each(myArray, myQueue) { element in
197 | // Each element in the array has this block called with it as a parameter.
198 | // Should be used on a concurrent queue.
199 | }
200 | ```
201 |
202 | ## Calling Asynchronous Functions Synchronously ##
203 |
204 | This section refers the idea of taking a naturally asynchronous function and calling
205 | it synchronously, generally for the purpose of chaining multiple asynchronous
206 | operations. This is essentially the same offering of PromiseKit but without
207 | the needless indentation and scope shuffle that comes with it.
208 |
209 | To see a practical use case, refer to the Quick Look example at the beginning of
210 | this document.
211 |
212 | When we talk about an asynchronous function, it must abide by these characteristics:
213 |
214 | * Returns ```Void```
215 | * Takes any number of input parameters
216 | * Has a single "completion" parameter that takes a function of the form ```(...) -> Void```
217 |
218 | These are all examples of suitable asynchronous functions:
219 |
220 | ```swift
221 | func getAlbum(named: String, handler: (album: PhotoAlbum?, error: NSError?) -> Void)
222 | func saveUser(completionHandler: (success: Bool) -> Void)
223 | func verifyUser(name: String, password: String, completion: (valid: Bool) -> Void)
224 |
225 | // Typical use of a function would look like:
226 | getAlbum("pics") { photo, error in
227 | // ...
228 | }
229 | ```
230 |
231 | With Brisk, you can use the ```<<+```, ```<<~``` or ```<<-``` operators to call your function in
232 | a way that blocks the calling thread until your function has called its completion
233 | handler.
234 |
235 | ```swift
236 | // <<+ will execute getAlbum on the main queue
237 | let (album, error) = <<+{ getAlbum("pics", handler: $0) }
238 |
239 | // <<~ will execute saveUser on the global concurrent background queue
240 | let success = <<~{ saveUser($0) }
241 |
242 | // <<- will execute verifyUser immediately in the current queue (note that the
243 | // current thread will wait for the completion handler to be called before
244 | // returning the final value.)
245 | let valid = <<-{ verifyUser("myname", password: "mypass", completion: $0) }
246 |
247 | // You can also specify *any* queue you want. Here saveUser is called on myQueue.
248 | let myQueue = dispatch_queue_create("myQueue", nil)
249 | let valid = <<~myQueue ~~~ { saveUser($0) }
250 | ```
251 |
252 | In all of the above examples, execution of the outer thread is paused until the completion
253 | handler ```$0``` is called. Once ```$0``` is called, the values passed into it are routed back
254 | to the original assignment operation.
255 |
256 | Note that the ```$0``` handler can accommodate any number of parameters (e.g. ```getAlbum```
257 | above can take ```album``` and ```error```), but it must be assigned to variables that
258 | create the same tuple. Also note that it is not possible to extract ```NSError``` parameters
259 | to transform them into do/try/catch methodology -- you will have to check the ```NSError```
260 | as part of the returned tuple.
261 |
262 | Also note that the outer thread WILL PAUSE until ```$0``` is called. This means that
263 | Brisk can only be used for functions that guarantee their completion handlers will
264 | be called at some deterministic point in the future. It is not suitable for open-ended
265 | asynchronous functions like ```NSNotification``` handlers.
266 |
267 | Another really important note: because of the requirement that ```$0``` is called, you should
268 | never use this pattern with optional functions unless you can guarantee they are not nil!
269 |
270 | ```swift
271 | func testFunction(handler: (Int -> Void)? = nil) {
272 | // This call will block forever if handler is nil!
273 | let z: Int = <<~{ handler?($0) }
274 | }
275 | ```
276 |
277 | ## Calling Synchronous Functions Asynchronously ##
278 |
279 | There are many reasons to call synchronous functions asynchronously. It's happening any
280 | time you see this pattern:
281 |
282 | ```swift
283 | dispatch_async(someQueue) {
284 | // Do something
285 | }
286 | ```
287 |
288 | The stylistic problem with the pattern above is when ```"// Do something"``` is a single function.
289 | You're burning three lines and an indentation scope just to route a single function call to
290 | another queue.
291 |
292 | An example of this is in the Quick Look example from the beginning of the documentation. This
293 | routing must take place each time the completion handler is called on the main queue. It
294 | has a negative impact on the readability of the overall function, since the actual function
295 | name gets buried in the scope of the dispatch. Wouldn't it be nice if that could be
296 | accomplished in one line, with the function name first?
297 |
298 | The ```~>>``` and ```+>>``` operators introduced in Brisk can be thought of as the
299 | synchronous->asynchronous translators. The main difference between the two is that
300 | the ```+>>``` operator dispatches to the main queue, while the ```~>>``` operator
301 | allows you to specify the queue (or use the concurrent background queue by default).
302 |
303 | For the examples below, consider the following normal synchronous functions:
304 |
305 | ```swift
306 | func syncReturnsVoid() { }
307 | func syncReturnsParam(p: Int) -> Int { return p+1 }
308 | func syncReturnsParamTuple(p: Int) -> (Int, String) { return (p+1, "\(p+1)") }
309 | ```
310 |
311 | Use the infix operator between a function and its parameters to quickly dispatch a synchronous function on
312 | another queue.
313 |
314 | ```swift
315 | dispatch_async(someQueue) {
316 |
317 | // syncReturnsVoid() is called on the main thread
318 | syncReturnsVoid +>> ()
319 |
320 | // syncReturnsParam(p: 3) is called on the main thread
321 | // Note in this case the return value is ignored!
322 | syncReturnsParam +>> (p: 3)
323 |
324 | // syncReturnsVoid() is called on the global concurrent background queue
325 | syncReturnsVoid ~>> ()
326 |
327 | // syncReturnsParam(p: 3) is called on the global concurrent background queue
328 | // Note in this case the return value is ignored!
329 | syncReturnsParam ~>> (p: 3)
330 |
331 | let otherQueue = dispatch_queue_create("otherQueue", nil)
332 |
333 | // syncReturnsVoid() is called on otherQueue
334 | syncReturnsVoid ~>> otherQueue ~>> ()
335 |
336 | // syncReturnsParam(p: 3) is called on otherQueue
337 | // Note in this case the return value is ignored!
338 | syncReturnsParam ~>> otherQueue ~>> (p: 3)
339 | }
340 | ```
341 |
342 | You can also use the operators in a postfix fashion for a more functional syntax:
343 |
344 | ```swift
345 | dispatch_async(someQueue) {
346 | let otherQueue = dispatch_queue_create("otherQueue", nil)
347 |
348 | // The following three lines are equivalent
349 | syncReturnsParam~>>.on(otherQueue).async(p: 3)
350 | syncReturnsParam~>>otherQueue~>>(p: 3)
351 | syncReturnsParam ~>> otherQueue ~>> (p: 3)
352 | }
353 | ```
354 |
355 | In all of the above examples, the return values were ignored. This is generally fine
356 | for the synchronous functions that return ```Void``` (like most completion handlers).
357 | Because the functions are called asynchronously, you have to process the return
358 | values asynchronously as well:
359 |
360 | ```swift
361 | dispatch_async(someQueue) {
362 |
363 | // syncReturnsParam(p: 3) is called on the main thread
364 | // Its response is also handled on the main thread
365 | syncReturnsParam +>> (p: 3) +>> { i in print(i) } // prints 4
366 |
367 | // syncReturnsParam(p: 3) is called on the main thread
368 | // Its response is handled on the global concurrent background queue
369 | // Note the positions and difference between +>> and ~>>
370 | syncReturnsParam +>> (p: 3) ~>> { i in print(i) } // prints 4
371 |
372 | // syncReturnsParamTuple(p: 3) is called on the global concurrent background queue
373 | // Its response is handled on an instantiated queue
374 | syncReturnsParamTuple ~>> (p: 3) ~>> otherQueue ~>> { iInt, iStr in print(pInt) }
375 |
376 | // Using the more functional style
377 | syncReturnsParam~>>.on(otherQueue).async(p: 3) +>> { i in print(i) }
378 | }
379 | ```
380 |
381 | ### Optionals ###
382 |
383 | When the function you are routing is an optional, you must use the ```?~>>``` and ```?+>>```
384 | operators when referencing the function:
385 |
386 | ```swift
387 | func myTest(param: Int, completionHandler: (Int -> Int)? = nil) {
388 |
389 | // These will cause a compiler error because the handler is optional:
390 | completionHandler +>> (param)
391 | completionHandler+>>.async(param) +>> { i in print(i) }
392 |
393 | // Instead use these:
394 | completionHandler ?+>> (param)
395 | completionHandler?~>>.async(param)
396 |
397 | // For anything past the initial function, use normal operators:
398 | // (+>> instead of ?+>>) --v
399 | completionHandler ?+>> (param) +>> { i in print(i) }
400 |
401 | }
402 | ```
403 |
404 | ### Routing a Block Synchronously to Another Queue ###
405 |
406 | Using the functional syntax, you can route a function (or any block) to another
407 | thread while the calling thread waits. This can be very useful for cases where
408 | we want to update UI, or some other main-thread-dependent resource from a background
409 | thread.
410 |
411 | ```swift
412 | dispatch_async(someQueue) {
413 | // In the middle of some background code we want to change the UI
414 |
415 | // This does it asynchronously (both are the same):
416 | { self.label.hidden = true }+>>();
417 | { self.label.hidden = true }+>>.async();
418 |
419 | // This does it synchronously (call waits until change is made)
420 | { self.label.hidden = true }+>>.sync();
421 |
422 | // The above statement is equivalent to
423 | dispatch_main_sync {
424 | self.label.hidden = true
425 | }
426 | }
427 | ```
428 |
429 | *Note that because of the Swift compiler, you may need to include a semicolon on the line
430 | before you create a statement with a block as the left-most expression.*
431 |
432 | ## Versioning ##
433 |
434 | To help with Cocoapods versioning syntax, all versions of Brisk compatible with Swift 2.2 will begin with Major/Minor 2.2. All versions comptible with Swift 2.3 will begin with Major/Minor 2.3. All versions compatible with Swift 3.0 will begin with Major/Minor 3.0, etc.
435 |
436 | This means your Cocoapod inclusion can look like:
437 |
438 | ```
439 | pod 'Brisk', '~> 2.2' # Latest version compatible with Swift 2.2
440 | pod 'Brisk', '~> 2.3' # Latest version compatible with Swift 2.3
441 | pod 'Brisk', '~> 3.0' # Latest version compatible with Swift 3.0
442 | ```
443 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | > NOTE
4 | >
5 | > Brisk is being mothballed due to general incompatibilities with modern version of Swift. I recommend checking out ReactiveSwift, which solves many of the issues that prompted me to make Brisk in the first place.
6 |
7 | Swift support for blocks and asynchronous code is powerful, but can lead to a maze of indented logic that
8 | quickly becomes unreadable and error-prone.
9 |
10 | Brisk offers two distinct but complimentary functions:
11 |
12 | 1. Provides shorthand operators for swiveling the concurrency of your functions (akin to async/await)
13 | 2. Extends ```DispatchQueue``` with several functions that help make standard usage a bit more concise.
14 |
15 | ```swift
16 | // Example: Making an asynchronous function be synchronous
17 | let (data, response, error) = <<-{ URLSession.shared.dataTask(url, completionHandler: $0).resume() }
18 | ```
19 |
20 | ## Versioning ##
21 |
22 | To help with Cocoapods versioning syntax, all versions of Brisk compatible with Swift 2.2 will begin with Major/Minor 2.2. All versions comptible with Swift 2.3 will begin with Major/Minor 2.3. All versions compatible with Swift 3.0 will begin with Major/Minor 3.0, etc.
23 |
24 | This means your Cocoapod inclusion can look like:
25 |
26 | ```
27 | pod 'Brisk', '~> 2.2' # Latest version compatible with Swift 2.2
28 | pod 'Brisk', '~> 2.3' # Latest version compatible with Swift 2.3
29 | pod 'Brisk', '~> 3.0' # Latest version compatible with Swift 3.0
30 | pod 'Brisk', '~> 3.1' # Latest version compatible with Swift 3.1
31 | ```
32 |
33 | > The Brisk API is different in Swift 2.x. Please refer to ```README_SWIFT2.md```
34 |
35 | ### Quick Look: Concurrency Swiveling ###
36 |
37 | Consider the following hypothetical asynchronous API:
38 |
39 | ```swift
40 | // API we're given:
41 | func findClosestPokemon(within: Double,
42 | completionHandler: (pokemon: Pokemon?, error: NSError?) -> Void)
43 |
44 | func countPokeballs(completionHandler: (number: Int?, error: NSError?) -> Void)
45 |
46 | func throwPokeballAt(pokemon: Pokemon,
47 | completionHandler: (success: Bool, error: NSError?) -> Void)
48 | ```
49 |
50 | Let's assume that all of the completion handlers are called on the main thread. We want to
51 | make this utility function:
52 |
53 | ```swift
54 | // Utility we want:
55 | func throwAtClosestPokemon(within: Double,
56 | completionHandler: (success: Bool, pokemon: Pokemon?, error: NSError?) -> Void)
57 | ```
58 |
59 | This function represents a common occurrence of chaining asynchronous functions into a helper utility for a single use case.
60 | Using only the standard GCD library, your function might look like this:
61 |
62 | ```swift
63 | // The old way...
64 | func throwAtClosestPokemon(within: Double,
65 | completionHandler: (success: Bool, pokemon: Pokemon?, error: NSError?) -> Void) {
66 | // Step 1
67 | findClosestPokemon(within: within) { pokemon, error in
68 | guard let p = pokemon where error == nil else {
69 | DispatchQueue.main.async {
70 | completionHandler(success: false, pokemon: nil, error: error)
71 | }
72 | return
73 | }
74 |
75 | // Step 2
76 | countPokeballs { number, error in
77 | guard let n = number where error == nil else {
78 | DispatchQueue.main.async {
79 | completionHandler(success: false, pokemon: nil, error: error)
80 | }
81 | return
82 | }
83 |
84 | // Step 3
85 | throwPokeballAt(pokemon: p) { success, error in
86 | DispatchQueue.main.async {
87 | completionHandler(success: success, error: error)
88 | }
89 | }
90 | }
91 | }
92 | }
93 | ```
94 |
95 | Yikes! It can quickly look even worse if your async logic needs to branch. Let's look at how scoping/flow works with Brisk:
96 |
97 | ```swift
98 | // The new way...
99 | func throwAtClosestPokemon(within: Double,
100 | completionHandler: (success: Bool, pokemon: Pokemon?, error: NSError?) -> Void) {
101 |
102 | // Run everything inside a specified async queue, or DispatchQueue.global()
103 | myQueue.async {
104 |
105 | // Step 1
106 | let (pokemon, error) = <<+{ findClosestPokemon(within: within, completionHandler: $0) }
107 | guard let p = pokemon where error == nil else {
108 | return completionHandler +>> (success: false, error: error)
109 | }
110 |
111 | // Step 2
112 | let (number, error2) = <<+{ countPokeballs($0) }
113 | guard let n = number where error2 == nil else {
114 | return completionHandler +>> (success: false, error: error2)
115 | }
116 |
117 | // Step 3
118 | let (success, error3) = <<+{ throwPokeballAt(pokemon: p, completionHandler: $0) }
119 | completionHandler +>> (success: success, error: error3)
120 | }
121 | }
122 | ```
123 |
124 | With Brisk the asynchronous functions can be coded using a seemingly-synchronous flow.
125 | The asynchronous nature of the methods is hidden behind the custom operators. *Unlike PromiseKit, all return values
126 | remain in scope as well*.
127 |
128 | ## Calling Asynchronous Functions Synchronously ##
129 |
130 | This section refers the idea of taking am asynchronous function and calling
131 | it synchronously, generally for the purpose of chaining multiple asynchronous
132 | operations. This is essentially the same offering of PromiseKit but without
133 | the needless indentation and scope shuffle that comes with it.
134 |
135 | To see a practical use case, refer to the Quick Look example above.
136 |
137 | When we talk about an asynchronous function, it must abide by these characteristics:
138 |
139 | * Returns ```Void```
140 | * Takes any number of input parameters
141 | * Has a single "completion" parameter that takes a function of the form ```(...) -> Void```
142 |
143 | These are all examples of suitable asynchronous functions:
144 |
145 | ```swift
146 | func getAlbum(named: String, handler: (album: PhotoAlbum?, error: NSError?) -> Void)
147 | func saveUser(completionHandler: (success: Bool) -> Void)
148 | func verifyUser(name: String, password: String, completion: (valid: Bool) -> Void)
149 |
150 | // Typical use of a function would look like:
151 | getAlbum("pics") { photo, error in
152 | // ...
153 | }
154 | ```
155 |
156 | With Brisk, you can use the ```<<+```, ```<<~``` or ```<<-``` operators to call your function in
157 | a way that blocks the calling thread until your function has called its completion
158 | handler.
159 |
160 | ```swift
161 | // <<+ will execute getAlbum on the main queue
162 | let (album, error) = <<+{ getAlbum("pics", handler: $0) }
163 |
164 | // <<~ will execute saveUser on the global concurrent background queue
165 | let success = <<~{ saveUser($0) }
166 |
167 | // <<- will execute verifyUser immediately in the current queue (note that the
168 | // current thread will wait for the completion handler to be called before
169 | // returning the final value.)
170 | let valid = <<-{ verifyUser("myname", password: "mypass", completion: $0) }
171 |
172 | // You can also specify *any* queue you want. Here saveUser is called on myQueue.
173 | let myQueue = dispatch_queue_create("myQueue", nil)
174 | let valid = <<~myQueue ~~~ { saveUser($0) }
175 | ```
176 |
177 | > Tip: Use ```<<+``` for functions that
178 | > need to be called on the main thread (like UI updates). Use ```<<-``` for others.
179 |
180 | In all of the above examples, execution of the outer thread is paused until the completion
181 | handler ```$0``` is called. Once ```$0``` is called, the values passed into it are routed back
182 | to the original assignment operation.
183 |
184 | Note that the ```$0``` handler can accommodate any number of parameters (e.g. ```getAlbum```
185 | above can take ```album``` and ```error```), *but it must be assigned to a variable that
186 | of the same tuple*. Also note that it is not possible to extract ```NSError``` parameters
187 | to transform them into do/try/catch methodology -- you will have to check the ```NSError```
188 | as part of the returned tuple.
189 |
190 | **Also note that the outer thread *WILL WAIT* until ```$0``` is called.** This means that
191 | Brisk can only be used for functions that guarantee their completion handlers will
192 | be called at some deterministic point in the future. It is not suitable for open-ended
193 | asynchronous functions like ```NSNotification``` handlers.
194 |
195 |
196 | ## Calling Synchronous Functions Asynchronously ##
197 |
198 | There are many reasons to call synchronous functions asynchronously. It happens any
199 | time you see this pattern:
200 |
201 | ```swift
202 | dispatch_async(someQueue) {
203 | completionHandler(..)
204 | }
205 | ```
206 |
207 | You're burning three lines and an indentation scope just to route a single function call to
208 | another queue.
209 |
210 | An example of this is in the Quick Look example from the beginning of the documentation. This
211 | routing must take place each time the completion handler is called on the main queue. It
212 | has a negative impact on the readability of the overall function, since the actual function
213 | name gets buried in the scope of the dispatch. Wouldn't it be nice if that could be
214 | accomplished in one line, with the function name first?
215 |
216 | The ```~>>``` and ```+>>``` operators introduced in Brisk can be thought of as the
217 | synchronous->asynchronous translators. The main difference between the two is that
218 | the ```+>>``` operator dispatches to the main queue, while the ```~>>``` operator
219 | allows you to specify the queue (or use the concurrent background queue by default).
220 |
221 | For the examples below, consider the following normal synchronous functions:
222 |
223 | ```swift
224 | func syncReturnsVoid() { }
225 | func syncReturnsParam(p: Int) -> Int { return p+1 }
226 | func syncReturnsParamTuple(p: Int) -> (Int, String) { return (p+1, "\(p+1)") }
227 | ```
228 |
229 | Use the infix operator between a function and its parameters to quickly dispatch a synchronous function on
230 | another queue.
231 |
232 | ```swift
233 | dispatch_async(someQueue) {
234 |
235 | // syncReturnsVoid() is called on the main thread
236 | syncReturnsVoid +>> ()
237 |
238 | // syncReturnsParam(p: 3) is called on the main thread
239 | // Note in this case the return value is ignored!
240 | syncReturnsParam +>> (p: 3)
241 |
242 | // syncReturnsVoid() is called on the global concurrent background queue
243 | syncReturnsVoid ~>> ()
244 |
245 | // syncReturnsParam(p: 3) is called on the global concurrent background queue
246 | // Note in this case the return value is ignored!
247 | syncReturnsParam ~>> (p: 3)
248 |
249 | let otherQueue = dispatch_queue_create("otherQueue", nil)
250 |
251 | // syncReturnsVoid() is called on otherQueue
252 | syncReturnsVoid ~>> otherQueue ~>> ()
253 |
254 | // syncReturnsParam(p: 3) is called on otherQueue
255 | // Note in this case the return value is ignored!
256 | syncReturnsParam ~>> otherQueue ~>> (p: 3)
257 | }
258 | ```
259 |
260 | You can also use the operators in a postfix fashion for a more functional syntax:
261 |
262 | ```swift
263 | dispatch_async(someQueue) {
264 | let otherQueue = dispatch_queue_create("otherQueue", nil)
265 |
266 | // The following three lines are equivalent
267 | syncReturnsParam~>>.on(otherQueue).async(p: 3)
268 | syncReturnsParam~>>otherQueue~>>(p: 3)
269 | syncReturnsParam ~>> otherQueue ~>> (p: 3)
270 | }
271 | ```
272 |
273 | In all of the above examples, the return values were ignored. This is generally fine
274 | for the synchronous functions that return ```Void``` (like most completion handlers).
275 | Because the functions are called asynchronously, you have to process the return
276 | values asynchronously as well:
277 |
278 | ```swift
279 | dispatch_async(someQueue) {
280 |
281 | // syncReturnsParam(p: 3) is called on the main thread
282 | // Its response is also handled on the main thread
283 | syncReturnsParam +>> (p: 3) +>> { i in print(i) } // prints 4
284 |
285 | // syncReturnsParam(p: 3) is called on the main thread
286 | // Its response is handled on the global concurrent background queue
287 | // Note the positions and difference between +>> and ~>>
288 | syncReturnsParam +>> (p: 3) ~>> { i in print(i) } // prints 4
289 |
290 | // syncReturnsParamTuple(p: 3) is called on the global concurrent background queue
291 | // Its response is handled on an instantiated queue
292 | syncReturnsParamTuple ~>> (p: 3) ~>> otherQueue ~>> { iInt, iStr in print(pInt) }
293 |
294 | // Using the more functional style
295 | syncReturnsParam~>>.on(otherQueue).async(p: 3) +>> { i in print(i) }
296 | }
297 | ```
298 |
299 | ### Optionals ###
300 |
301 | When the function you are routing is an optional, you must use the ```?~>>``` and ```?+>>```
302 | operators when referencing the function:
303 |
304 | ```swift
305 | func myTest(param: Int, completionHandler: (Int -> Int)? = nil) {
306 |
307 | // These will cause a compiler error because the handler is optional:
308 | completionHandler +>> (param)
309 | completionHandler+>>.async(param) +>> { i in print(i) }
310 |
311 | // Instead use these:
312 | completionHandler ?+>> (param)
313 | completionHandler?~>>.async(param)
314 |
315 | // For anything past the initial function, use normal operators:
316 | // (+>> instead of ?+>>) --v
317 | completionHandler ?+>> (param) +>> { i in print(i) }
318 |
319 | }
320 | ```
321 |
322 | > Note that if you were using the syntax for optionals
323 | > ```completionHandler?~>>.main.async(param)```
324 | > -- Swift 3.1 does not allow ? characters at the beginning of postfix
325 | > operators so you can no longer explicitly choose a queue name
326 | > like ".main". You can still use queues implicitly with the operator, like
327 | > ```completionHandler?+>>.async(param)```
328 |
329 |
330 | ## Swift 3.x LibDispatch Additions ##
331 |
332 | Brisk extensions ```DispatchQueue``` with functions that make the ```async``` function
333 | more concise:
334 |
335 | ```swift
336 | /// LibDispatch:
337 | func asyncAfter(deadline: DispatchTime,
338 | qos: DispatchQoS = default,
339 | flags: DispatchWorkItemFlags = default,
340 | execute: () -> Void)
341 |
342 | // Brisk allows you to specify time/intervals as a Double instead of DispatchTime.
343 | // It also allows you to capture the timer used to dispatch the block, in case
344 | // you want to cancel it.
345 | func async(after seconds: Double,
346 | leeway: QuickDispatchTimeInterval? = nil,
347 | qos: DispatchQoS = .default,
348 | flags: DispatchWorkItemFlags = [],
349 | execute block: @escaping () -> Void) -> DispatchSourceTimer
350 | ```
351 |
352 | Also consider scheduling a block to run repeatedly at an interval:
353 |
354 | ```swift
355 | // LibDispatch Requires:
356 | let timer = DispatchSource.makeTimerSource(flags: ..., queue: ...)
357 | timer.setEventHandler(qos: ..., flags: ..., handler: ...)
358 | timer.scheduleRepeating(deadline: ..., interval: ..., leeway: ...)
359 | timer.resume()
360 |
361 | // Brisk allows you to schedule timers in one function, and passes the timer
362 | // into the block so it can be canceled based on logic inside or outside the handler.
363 | func async(every interval: Double,
364 | startingIn: Double? = nil,
365 | startingAt: NSDate? = nil,
366 | leeway: QuickDispatchTimeInterval? = nil,
367 | qos: DispatchQoS = .default,
368 | flags: DispatchWorkItemFlags = [],
369 | execute block: @escaping (_ tmr: DispatchSourceTimer) -> Void) -> DispatchSourceTimer
370 |
371 | ```
372 |
373 | Another new function allows you to coalesce multiple async calls into a single execution,
374 | based on an ```operationId```. This is useful when several simultaneous asynchronous
375 | actions want to trigger a block to occur (but you only want that block to occur once).
376 |
377 | ```swift
378 | func once(operationId: String,
379 | after interval: Double? = nil,
380 | at date: NSDate? = nil,
381 | leeway: QuickDispatchTimeInterval? = nil,
382 | qos: DispatchQoS = .default,
383 | flags: DispatchWorkItemFlags = [],
384 | execute block: @escaping () -> Void) -> DispatchSourceTimer
385 | ```
386 |
387 | There are several variations of the above functions. See ```BriskDispatch.swift``` for more details.
388 |
389 | ## Deprecated Swift 2.x GCD Additions ##
390 |
391 | The following code examples show the GCD additions provided by Brisk for the Swift 2.x syntax. These are
392 | fairly self-documenting. More information about each method can be found in its comment section. They are
393 | included in the Swift 3.x release for backwards compatibility.
394 |
395 | ```swift
396 | dispatch_main_async {
397 | // Block runs on the main queue
398 | }
399 |
400 | dispatch_main_sync {
401 | // Block runs on the main queue; this function does not return until
402 | // the block completes.
403 | }
404 |
405 | dispatch_bg_async {
406 | // Block runs on the global concurrent background queue
407 | }
408 |
409 | dispatch_async("myNewQueue") {
410 | // Block runs on a brisk-created serial queue with the specified string ID.
411 | // Calling this function multiple times with the same string will reuse the
412 | // named queue. Useful for dynamic throw-away serial queues.
413 | }
414 |
415 | dispatch_main_after(2.0) {
416 | dispatch_after(2.0, myQueue) {
417 | // Block is called on specified queue after specified number of seconds using
418 | // a loose leeway (+/- 0.1 seconds).
419 | }
420 |
421 | dispatch_main_after_exactly(2.0) {
422 | dispatch_after_exactly(2.0, myQueue) {
423 | // Block is called on specified queue after specified number of seconds using
424 | // as tight a timer leeway as possible. Useful for animation timing but
425 | // uses more battery power.
426 | }
427 |
428 | dispatch_main_every(2.0) { timer in
429 | dispatch_every(2.0, myQueue) { timer in
430 | dispatch_main_every_exact(2.0) { timer in
431 | dispatch_every_exact(2.0, myQueue) { timer in
432 | // Block is run on specified thread every N seconds.
433 | // Stop the timer with:
434 | dispatch_source_cancel(timer)
435 | }
436 |
437 | dispatch_main_once_after(2.0, "myOperationId") {
438 | dispatch_once_after(2.0, myQueue, "myOperationId") {
439 | // Block runs after specified time on specified queue. The block is
440 | // only executed ONCE -- repeat calls to this function with the same
441 | // operation ID will reset its internal timer instead of calling the
442 | // block again. Useful for calling a completion block after several
443 | // disparate asynchronous methods (e.g. saving the database to disk
444 | // after downloading multiple records on separate threads.)
445 | }
446 |
447 | dispatch_each(myArray, myQueue) { element in
448 | // Each element in the array has this block called with it as a parameter.
449 | // Should be used on a concurrent queue.
450 | }
451 | ```
452 |
--------------------------------------------------------------------------------
/Brisk.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 35057D721D60B930004BBF6D /* BriskRaise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35057D711D60B930004BBF6D /* BriskRaise.swift */; };
11 | 35057D741D60BCF9004BBF6D /* BriskGate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35057D731D60BCF9004BBF6D /* BriskGate.swift */; };
12 | 356AFC631D5E618700D18479 /* Brisk.h in Headers */ = {isa = PBXBuildFile; fileRef = 356AFC621D5E618700D18479 /* Brisk.h */; settings = {ATTRIBUTES = (Public, ); }; };
13 | 356AFC6A1D5E618700D18479 /* Brisk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 356AFC5F1D5E618700D18479 /* Brisk.framework */; };
14 | 356AFC6F1D5E618700D18479 /* BriskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356AFC6E1D5E618700D18479 /* BriskTests.swift */; };
15 | 356AFC7C1D5E623E00D18479 /* BriskLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356AFC7B1D5E623E00D18479 /* BriskLock.swift */; };
16 | 356AFC7E1D5E63C900D18479 /* BriskSync2Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356AFC7D1D5E63C900D18479 /* BriskSync2Async.swift */; };
17 | 356AFC801D5E63D100D18479 /* BriskAsync2Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356AFC7F1D5E63D100D18479 /* BriskAsync2Sync.swift */; };
18 | 356AFC821D5E8CAC00D18479 /* Spin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356AFC811D5E8CAC00D18479 /* Spin.swift */; };
19 | 3585ABB41D8624E10063299C /* BriskGCD_Swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3585ABB31D8624E10063299C /* BriskGCD_Swift2.swift */; };
20 | 3585ABB61D86265E0063299C /* BriskDispatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3585ABB51D86265E0063299C /* BriskDispatch.swift */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXContainerItemProxy section */
24 | 356AFC6B1D5E618700D18479 /* PBXContainerItemProxy */ = {
25 | isa = PBXContainerItemProxy;
26 | containerPortal = 356AFC561D5E618700D18479 /* Project object */;
27 | proxyType = 1;
28 | remoteGlobalIDString = 356AFC5E1D5E618700D18479;
29 | remoteInfo = Brisk;
30 | };
31 | /* End PBXContainerItemProxy section */
32 |
33 | /* Begin PBXFileReference section */
34 | 35057D711D60B930004BBF6D /* BriskRaise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BriskRaise.swift; sourceTree = ""; };
35 | 35057D731D60BCF9004BBF6D /* BriskGate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BriskGate.swift; sourceTree = ""; };
36 | 35057D761D60EE0B004BBF6D /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; };
37 | 35057D781D60EE81004BBF6D /* Brisk.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Brisk.podspec; sourceTree = SOURCE_ROOT; };
38 | 35057D7A1D60EFEE004BBF6D /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = SOURCE_ROOT; };
39 | 35057D7B1D60EFEE004BBF6D /* CONTRIBUTORS.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTORS.md; sourceTree = SOURCE_ROOT; };
40 | 35057D7E1D6132A8004BBF6D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
41 | 356AFC5F1D5E618700D18479 /* Brisk.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Brisk.framework; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 356AFC621D5E618700D18479 /* Brisk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Brisk.h; sourceTree = ""; };
43 | 356AFC641D5E618700D18479 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
44 | 356AFC691D5E618700D18479 /* BriskTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BriskTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
45 | 356AFC6E1D5E618700D18479 /* BriskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BriskTests.swift; sourceTree = ""; };
46 | 356AFC701D5E618700D18479 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
47 | 356AFC7B1D5E623E00D18479 /* BriskLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BriskLock.swift; sourceTree = ""; };
48 | 356AFC7D1D5E63C900D18479 /* BriskSync2Async.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BriskSync2Async.swift; sourceTree = ""; };
49 | 356AFC7F1D5E63D100D18479 /* BriskAsync2Sync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BriskAsync2Sync.swift; sourceTree = ""; };
50 | 356AFC811D5E8CAC00D18479 /* Spin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spin.swift; sourceTree = ""; };
51 | 3585AB7C1D845A550063299C /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = SOURCE_ROOT; };
52 | 3585ABB01D85FE8F0063299C /* README_SWIFT2.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README_SWIFT2.md; sourceTree = SOURCE_ROOT; };
53 | 3585ABB31D8624E10063299C /* BriskGCD_Swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BriskGCD_Swift2.swift; sourceTree = ""; };
54 | 3585ABB51D86265E0063299C /* BriskDispatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BriskDispatch.swift; sourceTree = ""; };
55 | /* End PBXFileReference section */
56 |
57 | /* Begin PBXFrameworksBuildPhase section */
58 | 356AFC5B1D5E618700D18479 /* Frameworks */ = {
59 | isa = PBXFrameworksBuildPhase;
60 | buildActionMask = 2147483647;
61 | files = (
62 | );
63 | runOnlyForDeploymentPostprocessing = 0;
64 | };
65 | 356AFC661D5E618700D18479 /* Frameworks */ = {
66 | isa = PBXFrameworksBuildPhase;
67 | buildActionMask = 2147483647;
68 | files = (
69 | 356AFC6A1D5E618700D18479 /* Brisk.framework in Frameworks */,
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | /* End PBXFrameworksBuildPhase section */
74 |
75 | /* Begin PBXGroup section */
76 | 356AFC551D5E618700D18479 = {
77 | isa = PBXGroup;
78 | children = (
79 | 356AFC611D5E618700D18479 /* Brisk */,
80 | 356AFC6D1D5E618700D18479 /* BriskTests */,
81 | 356AFC601D5E618700D18479 /* Products */,
82 | );
83 | sourceTree = "";
84 | usesTabs = 0;
85 | };
86 | 356AFC601D5E618700D18479 /* Products */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 356AFC5F1D5E618700D18479 /* Brisk.framework */,
90 | 356AFC691D5E618700D18479 /* BriskTests.xctest */,
91 | );
92 | name = Products;
93 | sourceTree = "";
94 | };
95 | 356AFC611D5E618700D18479 /* Brisk */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 3585AB7C1D845A550063299C /* .travis.yml */,
99 | 35057D7E1D6132A8004BBF6D /* README.md */,
100 | 3585ABB01D85FE8F0063299C /* README_SWIFT2.md */,
101 | 35057D7A1D60EFEE004BBF6D /* CHANGELOG.md */,
102 | 35057D7B1D60EFEE004BBF6D /* CONTRIBUTORS.md */,
103 | 35057D781D60EE81004BBF6D /* Brisk.podspec */,
104 | 35057D761D60EE0B004BBF6D /* LICENSE */,
105 | 356AFC621D5E618700D18479 /* Brisk.h */,
106 | 356AFC641D5E618700D18479 /* Info.plist */,
107 | 356AFC7B1D5E623E00D18479 /* BriskLock.swift */,
108 | 356AFC7D1D5E63C900D18479 /* BriskSync2Async.swift */,
109 | 356AFC7F1D5E63D100D18479 /* BriskAsync2Sync.swift */,
110 | 3585ABB51D86265E0063299C /* BriskDispatch.swift */,
111 | 35057D711D60B930004BBF6D /* BriskRaise.swift */,
112 | 35057D731D60BCF9004BBF6D /* BriskGate.swift */,
113 | 3585ABB21D8624E10063299C /* Swift2 */,
114 | );
115 | path = Brisk;
116 | sourceTree = "";
117 | };
118 | 356AFC6D1D5E618700D18479 /* BriskTests */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 356AFC6E1D5E618700D18479 /* BriskTests.swift */,
122 | 356AFC811D5E8CAC00D18479 /* Spin.swift */,
123 | 356AFC701D5E618700D18479 /* Info.plist */,
124 | );
125 | path = BriskTests;
126 | sourceTree = "";
127 | };
128 | 3585ABB21D8624E10063299C /* Swift2 */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 3585ABB31D8624E10063299C /* BriskGCD_Swift2.swift */,
132 | );
133 | path = Swift2;
134 | sourceTree = "";
135 | };
136 | /* End PBXGroup section */
137 |
138 | /* Begin PBXHeadersBuildPhase section */
139 | 356AFC5C1D5E618700D18479 /* Headers */ = {
140 | isa = PBXHeadersBuildPhase;
141 | buildActionMask = 2147483647;
142 | files = (
143 | 356AFC631D5E618700D18479 /* Brisk.h in Headers */,
144 | );
145 | runOnlyForDeploymentPostprocessing = 0;
146 | };
147 | /* End PBXHeadersBuildPhase section */
148 |
149 | /* Begin PBXNativeTarget section */
150 | 356AFC5E1D5E618700D18479 /* Brisk */ = {
151 | isa = PBXNativeTarget;
152 | buildConfigurationList = 356AFC731D5E618700D18479 /* Build configuration list for PBXNativeTarget "Brisk" */;
153 | buildPhases = (
154 | 356AFC5A1D5E618700D18479 /* Sources */,
155 | 356AFC5B1D5E618700D18479 /* Frameworks */,
156 | 356AFC5C1D5E618700D18479 /* Headers */,
157 | 356AFC5D1D5E618700D18479 /* Resources */,
158 | );
159 | buildRules = (
160 | );
161 | dependencies = (
162 | );
163 | name = Brisk;
164 | productName = Brisk;
165 | productReference = 356AFC5F1D5E618700D18479 /* Brisk.framework */;
166 | productType = "com.apple.product-type.framework";
167 | };
168 | 356AFC681D5E618700D18479 /* BriskTests */ = {
169 | isa = PBXNativeTarget;
170 | buildConfigurationList = 356AFC761D5E618700D18479 /* Build configuration list for PBXNativeTarget "BriskTests" */;
171 | buildPhases = (
172 | 356AFC651D5E618700D18479 /* Sources */,
173 | 356AFC661D5E618700D18479 /* Frameworks */,
174 | 356AFC671D5E618700D18479 /* Resources */,
175 | );
176 | buildRules = (
177 | );
178 | dependencies = (
179 | 356AFC6C1D5E618700D18479 /* PBXTargetDependency */,
180 | );
181 | name = BriskTests;
182 | productName = BriskTests;
183 | productReference = 356AFC691D5E618700D18479 /* BriskTests.xctest */;
184 | productType = "com.apple.product-type.bundle.unit-test";
185 | };
186 | /* End PBXNativeTarget section */
187 |
188 | /* Begin PBXProject section */
189 | 356AFC561D5E618700D18479 /* Project object */ = {
190 | isa = PBXProject;
191 | attributes = {
192 | LastSwiftUpdateCheck = 0730;
193 | LastUpgradeCheck = 0800;
194 | ORGANIZATIONNAME = "Jason Fieldman";
195 | TargetAttributes = {
196 | 356AFC5E1D5E618700D18479 = {
197 | CreatedOnToolsVersion = 7.3;
198 | LastSwiftMigration = 0800;
199 | };
200 | 356AFC681D5E618700D18479 = {
201 | CreatedOnToolsVersion = 7.3;
202 | LastSwiftMigration = 0800;
203 | };
204 | };
205 | };
206 | buildConfigurationList = 356AFC591D5E618700D18479 /* Build configuration list for PBXProject "Brisk" */;
207 | compatibilityVersion = "Xcode 3.2";
208 | developmentRegion = English;
209 | hasScannedForEncodings = 0;
210 | knownRegions = (
211 | en,
212 | );
213 | mainGroup = 356AFC551D5E618700D18479;
214 | productRefGroup = 356AFC601D5E618700D18479 /* Products */;
215 | projectDirPath = "";
216 | projectRoot = "";
217 | targets = (
218 | 356AFC5E1D5E618700D18479 /* Brisk */,
219 | 356AFC681D5E618700D18479 /* BriskTests */,
220 | );
221 | };
222 | /* End PBXProject section */
223 |
224 | /* Begin PBXResourcesBuildPhase section */
225 | 356AFC5D1D5E618700D18479 /* Resources */ = {
226 | isa = PBXResourcesBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | );
230 | runOnlyForDeploymentPostprocessing = 0;
231 | };
232 | 356AFC671D5E618700D18479 /* Resources */ = {
233 | isa = PBXResourcesBuildPhase;
234 | buildActionMask = 2147483647;
235 | files = (
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | };
239 | /* End PBXResourcesBuildPhase section */
240 |
241 | /* Begin PBXSourcesBuildPhase section */
242 | 356AFC5A1D5E618700D18479 /* Sources */ = {
243 | isa = PBXSourcesBuildPhase;
244 | buildActionMask = 2147483647;
245 | files = (
246 | 356AFC801D5E63D100D18479 /* BriskAsync2Sync.swift in Sources */,
247 | 35057D721D60B930004BBF6D /* BriskRaise.swift in Sources */,
248 | 35057D741D60BCF9004BBF6D /* BriskGate.swift in Sources */,
249 | 3585ABB61D86265E0063299C /* BriskDispatch.swift in Sources */,
250 | 3585ABB41D8624E10063299C /* BriskGCD_Swift2.swift in Sources */,
251 | 356AFC7E1D5E63C900D18479 /* BriskSync2Async.swift in Sources */,
252 | 356AFC7C1D5E623E00D18479 /* BriskLock.swift in Sources */,
253 | );
254 | runOnlyForDeploymentPostprocessing = 0;
255 | };
256 | 356AFC651D5E618700D18479 /* Sources */ = {
257 | isa = PBXSourcesBuildPhase;
258 | buildActionMask = 2147483647;
259 | files = (
260 | 356AFC821D5E8CAC00D18479 /* Spin.swift in Sources */,
261 | 356AFC6F1D5E618700D18479 /* BriskTests.swift in Sources */,
262 | );
263 | runOnlyForDeploymentPostprocessing = 0;
264 | };
265 | /* End PBXSourcesBuildPhase section */
266 |
267 | /* Begin PBXTargetDependency section */
268 | 356AFC6C1D5E618700D18479 /* PBXTargetDependency */ = {
269 | isa = PBXTargetDependency;
270 | target = 356AFC5E1D5E618700D18479 /* Brisk */;
271 | targetProxy = 356AFC6B1D5E618700D18479 /* PBXContainerItemProxy */;
272 | };
273 | /* End PBXTargetDependency section */
274 |
275 | /* Begin XCBuildConfiguration section */
276 | 356AFC711D5E618700D18479 /* Debug */ = {
277 | isa = XCBuildConfiguration;
278 | buildSettings = {
279 | ALWAYS_SEARCH_USER_PATHS = NO;
280 | CLANG_ANALYZER_NONNULL = YES;
281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
282 | CLANG_CXX_LIBRARY = "libc++";
283 | CLANG_ENABLE_MODULES = YES;
284 | CLANG_ENABLE_OBJC_ARC = YES;
285 | CLANG_WARN_BOOL_CONVERSION = YES;
286 | CLANG_WARN_CONSTANT_CONVERSION = YES;
287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
288 | CLANG_WARN_EMPTY_BODY = YES;
289 | CLANG_WARN_ENUM_CONVERSION = YES;
290 | CLANG_WARN_INFINITE_RECURSION = YES;
291 | CLANG_WARN_INT_CONVERSION = YES;
292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
293 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
294 | CLANG_WARN_UNREACHABLE_CODE = YES;
295 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
296 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
297 | COPY_PHASE_STRIP = NO;
298 | CURRENT_PROJECT_VERSION = 1;
299 | DEBUG_INFORMATION_FORMAT = dwarf;
300 | ENABLE_STRICT_OBJC_MSGSEND = YES;
301 | ENABLE_TESTABILITY = YES;
302 | GCC_C_LANGUAGE_STANDARD = gnu99;
303 | GCC_DYNAMIC_NO_PIC = NO;
304 | GCC_NO_COMMON_BLOCKS = YES;
305 | GCC_OPTIMIZATION_LEVEL = 0;
306 | GCC_PREPROCESSOR_DEFINITIONS = (
307 | "DEBUG=1",
308 | "$(inherited)",
309 | );
310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
312 | GCC_WARN_UNDECLARED_SELECTOR = YES;
313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
314 | GCC_WARN_UNUSED_FUNCTION = YES;
315 | GCC_WARN_UNUSED_VARIABLE = YES;
316 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
317 | MTL_ENABLE_DEBUG_INFO = YES;
318 | ONLY_ACTIVE_ARCH = YES;
319 | SDKROOT = iphoneos;
320 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
321 | SWIFT_VERSION = 3.0;
322 | TARGETED_DEVICE_FAMILY = "1,2";
323 | VERSIONING_SYSTEM = "apple-generic";
324 | VERSION_INFO_PREFIX = "";
325 | };
326 | name = Debug;
327 | };
328 | 356AFC721D5E618700D18479 /* Release */ = {
329 | isa = XCBuildConfiguration;
330 | buildSettings = {
331 | ALWAYS_SEARCH_USER_PATHS = NO;
332 | CLANG_ANALYZER_NONNULL = YES;
333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_WARN_BOOL_CONVERSION = YES;
338 | CLANG_WARN_CONSTANT_CONVERSION = YES;
339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
340 | CLANG_WARN_EMPTY_BODY = YES;
341 | CLANG_WARN_ENUM_CONVERSION = YES;
342 | CLANG_WARN_INFINITE_RECURSION = YES;
343 | CLANG_WARN_INT_CONVERSION = YES;
344 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
345 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
346 | CLANG_WARN_UNREACHABLE_CODE = YES;
347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
348 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
349 | COPY_PHASE_STRIP = NO;
350 | CURRENT_PROJECT_VERSION = 1;
351 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
352 | ENABLE_NS_ASSERTIONS = NO;
353 | ENABLE_STRICT_OBJC_MSGSEND = YES;
354 | GCC_C_LANGUAGE_STANDARD = gnu99;
355 | GCC_NO_COMMON_BLOCKS = YES;
356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
358 | GCC_WARN_UNDECLARED_SELECTOR = YES;
359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
360 | GCC_WARN_UNUSED_FUNCTION = YES;
361 | GCC_WARN_UNUSED_VARIABLE = YES;
362 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
363 | MTL_ENABLE_DEBUG_INFO = NO;
364 | SDKROOT = iphoneos;
365 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
366 | SWIFT_VERSION = 3.0;
367 | TARGETED_DEVICE_FAMILY = "1,2";
368 | VALIDATE_PRODUCT = YES;
369 | VERSIONING_SYSTEM = "apple-generic";
370 | VERSION_INFO_PREFIX = "";
371 | };
372 | name = Release;
373 | };
374 | 356AFC741D5E618700D18479 /* Debug */ = {
375 | isa = XCBuildConfiguration;
376 | buildSettings = {
377 | CLANG_ENABLE_MODULES = YES;
378 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
379 | DEFINES_MODULE = YES;
380 | DYLIB_COMPATIBILITY_VERSION = 1;
381 | DYLIB_CURRENT_VERSION = 1;
382 | DYLIB_INSTALL_NAME_BASE = "@rpath";
383 | INFOPLIST_FILE = Brisk/Info.plist;
384 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
385 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
386 | PRODUCT_BUNDLE_IDENTIFIER = org.fieldman.Brisk;
387 | PRODUCT_NAME = "$(TARGET_NAME)";
388 | SKIP_INSTALL = YES;
389 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
390 | SWIFT_VERSION = 3.0;
391 | };
392 | name = Debug;
393 | };
394 | 356AFC751D5E618700D18479 /* Release */ = {
395 | isa = XCBuildConfiguration;
396 | buildSettings = {
397 | CLANG_ENABLE_MODULES = YES;
398 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
399 | DEFINES_MODULE = YES;
400 | DYLIB_COMPATIBILITY_VERSION = 1;
401 | DYLIB_CURRENT_VERSION = 1;
402 | DYLIB_INSTALL_NAME_BASE = "@rpath";
403 | INFOPLIST_FILE = Brisk/Info.plist;
404 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
406 | PRODUCT_BUNDLE_IDENTIFIER = org.fieldman.Brisk;
407 | PRODUCT_NAME = "$(TARGET_NAME)";
408 | SKIP_INSTALL = YES;
409 | SWIFT_VERSION = 3.0;
410 | };
411 | name = Release;
412 | };
413 | 356AFC771D5E618700D18479 /* Debug */ = {
414 | isa = XCBuildConfiguration;
415 | buildSettings = {
416 | INFOPLIST_FILE = BriskTests/Info.plist;
417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
418 | PRODUCT_BUNDLE_IDENTIFIER = org.fieldman.BriskTests;
419 | PRODUCT_NAME = "$(TARGET_NAME)";
420 | SWIFT_VERSION = 3.0;
421 | };
422 | name = Debug;
423 | };
424 | 356AFC781D5E618700D18479 /* Release */ = {
425 | isa = XCBuildConfiguration;
426 | buildSettings = {
427 | INFOPLIST_FILE = BriskTests/Info.plist;
428 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
429 | PRODUCT_BUNDLE_IDENTIFIER = org.fieldman.BriskTests;
430 | PRODUCT_NAME = "$(TARGET_NAME)";
431 | SWIFT_VERSION = 3.0;
432 | };
433 | name = Release;
434 | };
435 | /* End XCBuildConfiguration section */
436 |
437 | /* Begin XCConfigurationList section */
438 | 356AFC591D5E618700D18479 /* Build configuration list for PBXProject "Brisk" */ = {
439 | isa = XCConfigurationList;
440 | buildConfigurations = (
441 | 356AFC711D5E618700D18479 /* Debug */,
442 | 356AFC721D5E618700D18479 /* Release */,
443 | );
444 | defaultConfigurationIsVisible = 0;
445 | defaultConfigurationName = Release;
446 | };
447 | 356AFC731D5E618700D18479 /* Build configuration list for PBXNativeTarget "Brisk" */ = {
448 | isa = XCConfigurationList;
449 | buildConfigurations = (
450 | 356AFC741D5E618700D18479 /* Debug */,
451 | 356AFC751D5E618700D18479 /* Release */,
452 | );
453 | defaultConfigurationIsVisible = 0;
454 | defaultConfigurationName = Release;
455 | };
456 | 356AFC761D5E618700D18479 /* Build configuration list for PBXNativeTarget "BriskTests" */ = {
457 | isa = XCConfigurationList;
458 | buildConfigurations = (
459 | 356AFC771D5E618700D18479 /* Debug */,
460 | 356AFC781D5E618700D18479 /* Release */,
461 | );
462 | defaultConfigurationIsVisible = 0;
463 | defaultConfigurationName = Release;
464 | };
465 | /* End XCConfigurationList section */
466 | };
467 | rootObject = 356AFC561D5E618700D18479 /* Project object */;
468 | }
469 |
--------------------------------------------------------------------------------
/Brisk/BriskDispatch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskDispatch.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 |
29 | // These are defined in the main library so that they can be accessed in the unit tests
30 | internal let mainQueue = DispatchQueue.main
31 | internal let backgroundQueue = DispatchQueue.global(qos: .default)
32 |
33 |
34 | public protocol QuickDispatchTimeInterval {
35 | /// Returns the receiver as a DispatchTimeInterval
36 | func asDispatchTimeInterval() -> DispatchTimeInterval
37 | }
38 |
39 |
40 | extension Double: QuickDispatchTimeInterval {
41 | public func asDispatchTimeInterval() -> DispatchTimeInterval {
42 | return DispatchTimeInterval.nanoseconds(Int(self * Double(NSEC_PER_SEC)))
43 | }
44 | }
45 |
46 | extension Float: QuickDispatchTimeInterval {
47 | public func asDispatchTimeInterval() -> DispatchTimeInterval {
48 | return DispatchTimeInterval.nanoseconds(Int(Double(self) * Double(NSEC_PER_SEC)))
49 | }
50 | }
51 |
52 | extension Int: QuickDispatchTimeInterval {
53 | public func asDispatchTimeInterval() -> DispatchTimeInterval {
54 | return DispatchTimeInterval.seconds(self)
55 | }
56 | }
57 |
58 |
59 | // Data Structures for DispatchQueue.once
60 | private var operationTimerForId: [String : DispatchSourceTimer] = [:]
61 | private var operationTimerLock: NSRecursiveLock = NSRecursiveLock()
62 |
63 |
64 | public extension DispatchQueue {
65 |
66 | /// Dispatch a block asynchronously on the receiving queue after a period of time. This method
67 | /// takes parameters that allow more straightforward and readable code.
68 | /// The DispatchSourceTimer is returned for reference, but can be ignored.
69 | ///
70 | /// - parameter after: The number of seconds before the block is triggered.
71 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
72 | /// will use the default if unspecified.
73 | /// - parameter qos: The qos to use for the executing block.
74 | /// - parameter flags: The DispatchWorkItemFlags for the executing block.
75 | /// - parameter execute: The block to run after the specified time on the receiving queue.
76 | @discardableResult public func async(after seconds: Double,
77 | leeway: QuickDispatchTimeInterval? = nil,
78 | qos: DispatchQoS = .default,
79 | flags: DispatchWorkItemFlags = [],
80 | execute block: @escaping () -> Void) -> DispatchSourceTimer {
81 |
82 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
83 | timer.setEventHandler(qos: qos, flags: flags) {
84 | timer.setEventHandler(handler: nil)
85 | block()
86 | }
87 |
88 | timer.setCancelHandler { [weak timer] in
89 | timer?.setEventHandler(handler: nil)
90 | }
91 |
92 | if let leeway = leeway {
93 | timer.scheduleOneshot(deadline: DispatchTime.now() + seconds, leeway: leeway.asDispatchTimeInterval())
94 | } else {
95 | timer.scheduleOneshot(deadline: DispatchTime.now() + seconds)
96 | }
97 |
98 | timer.resume()
99 | return timer
100 | }
101 |
102 |
103 |
104 | /// Dispatch a block asynchronously on the receiving queue after a period of time. This method
105 | /// takes parameters that allow more straightforward and readable code.
106 | /// The DispatchSourceTimer is returned for reference, but can be ignored.
107 | ///
108 | /// - parameter after: The number of seconds before the block is triggered.
109 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
110 | /// will use the default if unspecified.
111 | /// - parameter execute: The item to execute after the specified time on the receiving queue.
112 | @discardableResult public func async(after seconds: Double,
113 | leeway: QuickDispatchTimeInterval? = nil,
114 | execute item: DispatchWorkItem) -> DispatchSourceTimer {
115 |
116 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
117 | timer.setEventHandler {
118 | timer.setEventHandler(handler: nil)
119 | item.perform()
120 | }
121 |
122 | timer.setCancelHandler { [weak timer] in
123 | timer?.setEventHandler(handler: nil)
124 | }
125 |
126 | if let leeway = leeway {
127 | timer.scheduleOneshot(deadline: DispatchTime.now() + seconds, leeway: leeway.asDispatchTimeInterval())
128 | } else {
129 | timer.scheduleOneshot(deadline: DispatchTime.now() + seconds)
130 | }
131 |
132 | timer.resume()
133 | return timer
134 | }
135 |
136 |
137 |
138 | /// Dispatch a block asynchronously on the receiving queue at a specific date. This method
139 | /// takes parameters that allow more straightforward and readable code.
140 | /// The DispatchSourceTimer is returned for reference, but can be ignored.
141 | ///
142 | /// - parameter at: The date to trigger the block. If the date is before the current time
143 | /// it is triggered immediately.
144 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
145 | /// will use the default if unspecified.
146 | /// - parameter qos: The qos to use for the executing block.
147 | /// - parameter flags: The DispatchWorkItemFlags for the executing block.
148 | /// - parameter execute: The block to run after the specified time on the receiving queue.
149 | @discardableResult public func async(at date: NSDate,
150 | leeway: QuickDispatchTimeInterval? = nil,
151 | qos: DispatchQoS = .default,
152 | flags: DispatchWorkItemFlags = [],
153 | execute block: @escaping () -> Void) -> DispatchSourceTimer {
154 |
155 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
156 | timer.setEventHandler(qos: qos, flags: flags) {
157 | timer.setEventHandler(handler: nil)
158 | block()
159 | }
160 |
161 | timer.setCancelHandler { [weak timer] in
162 | timer?.setEventHandler(handler: nil)
163 | }
164 |
165 | let timeInterval = max(date.timeIntervalSinceNow, 0)
166 |
167 | if let leeway = leeway {
168 | timer.scheduleOneshot(wallDeadline: DispatchWallTime.now() + timeInterval, leeway: leeway.asDispatchTimeInterval())
169 | } else {
170 | timer.scheduleOneshot(wallDeadline: DispatchWallTime.now() + timeInterval)
171 | }
172 |
173 | timer.resume()
174 | return timer
175 | }
176 |
177 |
178 |
179 | /// Dispatch a block asynchronously on the receiving queue at a specific date. This method
180 | /// takes parameters that allow more straightforward and readable code.
181 | /// The DispatchSourceTimer is returned for reference, but can be ignored.
182 | ///
183 | /// - parameter at: The date to trigger the block. If the date is before the current time
184 | /// it is triggered immediately.
185 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
186 | /// will use the default if unspecified.
187 | /// - parameter execute: The item to execute after the specified time on the receiving queue.
188 | @discardableResult public func async(at date: NSDate,
189 | leeway: QuickDispatchTimeInterval? = nil,
190 | execute item: DispatchWorkItem) -> DispatchSourceTimer {
191 |
192 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
193 | timer.setEventHandler {
194 | timer.setEventHandler(handler: nil)
195 | item.perform()
196 | }
197 |
198 | timer.setCancelHandler { [weak timer] in
199 | timer?.setEventHandler(handler: nil)
200 | }
201 |
202 | let deadline = DispatchWallTime.now() + max(date.timeIntervalSinceNow, 0)
203 |
204 | if let leeway = leeway {
205 | timer.scheduleOneshot(wallDeadline: deadline, leeway: leeway.asDispatchTimeInterval())
206 | } else {
207 | timer.scheduleOneshot(wallDeadline: deadline)
208 | }
209 |
210 | timer.resume()
211 | return timer
212 | }
213 |
214 |
215 |
216 | /// Dispatch a block asynchronously on the receiving queue at a specified rate.
217 | /// The DispatchSourceTimer is returned for reference, but can be ignored. This
218 | /// version of the function takes a block with no arguments. It is considered
219 | /// a fatal error to pass both startingIn and startingAt parameters. If neither
220 | /// startingIn or startingAt are specified, the repetition will start after
221 | /// one interval.
222 | ///
223 | /// - parameter every: The interval to execute the block.
224 | /// - parameter startingIn: The number of seconds to begin the repetition.
225 | /// - parameter startingAt: The date at which to start the repetition. If the date is
226 | /// in the past it will start immediately.
227 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
228 | /// will use the default if unspecified.
229 | /// - parameter qos: The qos to use for the executing block.
230 | /// - parameter flags: The DispatchWorkItemFlags for the executing block.
231 | /// - parameter execute: The block to run after the specified time on the receiving queue.
232 | @discardableResult public func async(every interval: Double,
233 | startingIn: Double? = nil,
234 | startingAt: NSDate? = nil,
235 | leeway: QuickDispatchTimeInterval? = nil,
236 | qos: DispatchQoS = .default,
237 | flags: DispatchWorkItemFlags = [],
238 | execute block: @escaping () -> Void) -> DispatchSourceTimer {
239 |
240 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
241 | timer.setEventHandler(qos: qos, flags: flags) {
242 | _ = timer.isCancelled
243 | block()
244 | }
245 |
246 | timer.setCancelHandler { [weak timer] in
247 | timer?.setEventHandler(handler: nil)
248 | }
249 |
250 | guard startingIn == nil || startingAt == nil else {
251 | Brisk.brisk_raise("It is considered a fatal error to pass both startingIn and startingAt")
252 | }
253 |
254 | if let startingAt = startingAt {
255 | let deadline = DispatchWallTime.now() + max(startingAt.timeIntervalSinceNow, 0)
256 |
257 | if let leeway = leeway {
258 | timer.scheduleRepeating(wallDeadline: deadline, interval: interval, leeway: leeway.asDispatchTimeInterval())
259 | } else {
260 | timer.scheduleRepeating(wallDeadline: deadline, interval: interval)
261 | }
262 | } else {
263 | let deadline = DispatchTime.now() + (startingIn ?? interval)
264 |
265 | if let leeway = leeway {
266 | timer.scheduleRepeating(deadline: deadline, interval: interval, leeway: leeway.asDispatchTimeInterval())
267 | } else {
268 | timer.scheduleRepeating(deadline: deadline, interval: interval)
269 | }
270 | }
271 |
272 | timer.resume()
273 | return timer
274 | }
275 |
276 |
277 |
278 | /// Dispatch a block asynchronously on the receiving queue at a specified rate.
279 | /// The DispatchSourceTimer is returned for reference, but can be ignored. It is considered
280 | /// a fatal error to pass both startingIn and startingAt parameters. If neither
281 | /// startingIn or startingAt are specified, the repetition will start after
282 | /// one interval.
283 | ///
284 | /// This version of the function takes a block with the repeating timer as an argument.
285 | /// You can use this parameter to cancel the repetition from inside the block.
286 | ///
287 | /// - parameter every: The interval to execute the block.
288 | /// - parameter startingIn: The number of seconds to begin the repetition.
289 | /// - parameter startingAt: The date at which to start the repetition. If the date is
290 | /// in the past it will start immediately.
291 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
292 | /// will use the default if unspecified.
293 | /// - parameter qos: The qos to use for the executing block.
294 | /// - parameter flags: The DispatchWorkItemFlags for the executing block.
295 | /// - parameter execute: The block to run after the specified time on the receiving queue.
296 | @discardableResult public func async(every interval: Double,
297 | startingIn: Double? = nil,
298 | startingAt: NSDate? = nil,
299 | leeway: QuickDispatchTimeInterval? = nil,
300 | qos: DispatchQoS = .default,
301 | flags: DispatchWorkItemFlags = [],
302 | execute block: @escaping (_ timer: DispatchSourceTimer) -> Void) -> DispatchSourceTimer {
303 |
304 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
305 | timer.setEventHandler(qos: qos, flags: flags) {
306 | block(timer)
307 | }
308 |
309 | timer.setCancelHandler { [weak timer] in
310 | timer?.setEventHandler(handler: nil)
311 | }
312 |
313 | guard startingIn == nil || startingAt == nil else {
314 | Brisk.brisk_raise("It is considered a fatal error to pass both startingIn and startingAt")
315 | }
316 |
317 | if let startingAt = startingAt {
318 | let deadline = DispatchWallTime.now() + max(startingAt.timeIntervalSinceNow, 0)
319 |
320 | if let leeway = leeway {
321 | timer.scheduleRepeating(wallDeadline: deadline, interval: interval, leeway: leeway.asDispatchTimeInterval())
322 | } else {
323 | timer.scheduleRepeating(wallDeadline: deadline, interval: interval)
324 | }
325 | } else {
326 | let deadline = DispatchTime.now() + (startingIn ?? interval)
327 |
328 | if let leeway = leeway {
329 | timer.scheduleRepeating(deadline: deadline, interval: interval, leeway: leeway.asDispatchTimeInterval())
330 | } else {
331 | timer.scheduleRepeating(deadline: deadline, interval: interval)
332 | }
333 | }
334 |
335 | timer.resume()
336 | return timer
337 | }
338 |
339 |
340 |
341 | /// Dispatch a block asynchronously on the receiving queue at a specified rate.
342 | /// The DispatchSourceTimer is returned for reference, but can be ignored. This
343 | /// version of the function takes a block with no arguments. It is considered
344 | /// a fatal error to pass both startingIn and startingAt parameters. If neither
345 | /// startingIn or startingAt are specified, the repetition will start after
346 | /// one interval.
347 | ///
348 | /// - parameter every: The interval to execute the block.
349 | /// - parameter startingIn: The number of seconds to begin the repetition.
350 | /// - parameter startingAt: The date at which to start the repetition. If the date is
351 | /// in the past it will start immediately.
352 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
353 | /// will use the default if unspecified.
354 | /// - parameter qos: The qos to use for the executing block.
355 | /// - parameter flags: The DispatchWorkItemFlags for the executing block.
356 | /// - parameter execute: The block to run after the specified time on the receiving queue.
357 | @discardableResult public func async(every interval: Double,
358 | startingIn: Double? = nil,
359 | startingAt: NSDate? = nil,
360 | leeway: QuickDispatchTimeInterval? = nil,
361 | execute item: DispatchWorkItem) -> DispatchSourceTimer {
362 |
363 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
364 | timer.setEventHandler {
365 | _ = timer.isCancelled
366 | item.perform()
367 | }
368 |
369 | timer.setCancelHandler { [weak timer] in
370 | timer?.setEventHandler(handler: nil)
371 | }
372 |
373 | guard startingIn == nil || startingAt == nil else {
374 | Brisk.brisk_raise("It is considered a fatal error to pass both startingIn and startingAt")
375 | }
376 |
377 | if let startingAt = startingAt {
378 | let deadline = DispatchWallTime.now() + max(startingAt.timeIntervalSinceNow, 0)
379 |
380 | if let leeway = leeway {
381 | timer.scheduleRepeating(wallDeadline: deadline, interval: interval, leeway: leeway.asDispatchTimeInterval())
382 | } else {
383 | timer.scheduleRepeating(wallDeadline: deadline, interval: interval)
384 | }
385 | } else {
386 | let deadline = DispatchTime.now() + (startingIn ?? interval)
387 |
388 | if let leeway = leeway {
389 | timer.scheduleRepeating(deadline: deadline, interval: interval, leeway: leeway.asDispatchTimeInterval())
390 | } else {
391 | timer.scheduleRepeating(deadline: deadline, interval: interval)
392 | }
393 | }
394 |
395 | timer.resume()
396 | return timer
397 | }
398 |
399 |
400 |
401 | /// Dispatch a block asynchronously on the receiving queue once per operationId,
402 | /// no matter how many times this request is made. This is convenient way to
403 | /// coalesce many disparate triggers into a single finalizing block (e.g. saving
404 | /// a database to disk after many simultaneous async updates)
405 | ///
406 | /// Each time the function is called with an operationId that corresponds to a
407 | /// timer that hasn't triggered, the previous timer is canceled in favor of the
408 | /// new one.
409 | ///
410 | /// When a timer eventually triggers for an operationId, that operationId is cleared
411 | /// and is no longer associated with a timer.
412 | ///
413 | /// It is considered a fatal error to pass both after and at parameters.
414 | /// If neither after or at is specified, the operation is scheduled to run asap.
415 | ///
416 | /// - parameter operationId: The ID of the operation to execute.
417 | /// - parameter leeway: The leeway, in seconds, for the timer. This is optional and
418 | /// will use the default if unspecified.
419 | /// - parameter startingIn: The number of seconds to begin the repetition.
420 | /// - parameter startingAt: The date at which to start the repetition. If the date is
421 | /// in the past it will start immediately.
422 | /// - parameter qos: The qos to use for the executing block.
423 | /// - parameter flags: The DispatchWorkItemFlags for the executing block.
424 | /// - parameter execute: The block to run after the specified time on the receiving queue.
425 | @discardableResult public func once(operationId: String,
426 | after interval: Double? = nil,
427 | at date: NSDate? = nil,
428 | leeway: QuickDispatchTimeInterval? = nil,
429 | qos: DispatchQoS = .default,
430 | flags: DispatchWorkItemFlags = [],
431 | execute block: @escaping () -> Void) -> DispatchSourceTimer {
432 |
433 | let timer = DispatchSource.makeTimerSource(flags: [], queue: self)
434 | timer.setEventHandler(qos: qos, flags: flags) {
435 | operationTimerLock.lock()
436 | if let curTimer = operationTimerForId[operationId], curTimer === timer {
437 | operationTimerForId[operationId] = nil
438 | }
439 | operationTimerLock.unlock()
440 | block()
441 | }
442 |
443 | guard interval == nil || date == nil else {
444 | Brisk.brisk_raise("It is considered a fatal error to pass both 'after' and 'at'")
445 | }
446 |
447 | if let date = date {
448 | let deadline = DispatchWallTime.now() + max(date.timeIntervalSinceNow, 0)
449 |
450 | if let leeway = leeway {
451 | timer.scheduleOneshot(wallDeadline: deadline, leeway: leeway.asDispatchTimeInterval())
452 | } else {
453 | timer.scheduleOneshot(wallDeadline: deadline)
454 | }
455 | } else {
456 | let deadline = DispatchTime.now() + (interval ?? 0)
457 |
458 | if let leeway = leeway {
459 | timer.scheduleOneshot(deadline: deadline, leeway: leeway.asDispatchTimeInterval())
460 | } else {
461 | timer.scheduleOneshot(deadline: deadline)
462 | }
463 | }
464 |
465 | operationTimerLock.lock()
466 | if let existingTimer = operationTimerForId[operationId], !existingTimer.isCancelled {
467 | existingTimer.cancel()
468 | }
469 | operationTimerForId[operationId] = timer
470 | timer.resume()
471 | operationTimerLock.unlock()
472 |
473 | return timer
474 | }
475 | }
476 |
477 |
478 |
--------------------------------------------------------------------------------
/Brisk/BriskSync2Async.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskSync2Async.swift
3 | // Brisk
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 |
29 |
30 |
31 | // MARK: - Routing Object
32 |
33 |
34 | public class __BriskRoutingObj {
35 |
36 | // ---------- Properties ----------
37 |
38 | // The dispatch group used in various synchronizing routines
39 | fileprivate let dispatchGroup = DispatchGroup()
40 |
41 | // This is the actual function that we are routing
42 | fileprivate let wrappedFunction: (I) -> O
43 |
44 | // If we are routing the response, this catches the value
45 | fileprivate var response: O? = nil
46 |
47 | // This is the queue that the function will be executed on
48 | fileprivate var opQueue: DispatchQueue? = nil
49 |
50 | // This is the queue that the handler will execute on (if needed)
51 | fileprivate var handlerQueue: DispatchQueue? = nil
52 |
53 | // The lock used to synchronize various accesses
54 | fileprivate var lock: NSLock = NSLock()
55 |
56 | // Is this routing object available to perform its operation?
57 | // The routing objects may only perform their operations once, they should
58 | // NOT be retained and called a second time.
59 | fileprivate var operated: Bool = false
60 |
61 |
62 |
63 | // ---------- Init ------------
64 |
65 | // Instantiate ourselves with a function
66 | public init(function: @escaping (I) -> O, defaultOpQueue: DispatchQueue? = nil) {
67 | wrappedFunction = function
68 | opQueue = defaultOpQueue
69 | }
70 |
71 |
72 |
73 | // ---------- Queue Adjustments -------------
74 |
75 | /// Returns the current routing object set to execute its
76 | /// function on the main queue
77 | public var main: __BriskRoutingObj {
78 | self.opQueue = mainQueue
79 | return self
80 | }
81 |
82 | /// Returns the current routing object set to execute its
83 | /// function on the generic concurrent background queue
84 | public var background: __BriskRoutingObj {
85 | self.opQueue = backgroundQueue
86 | return self
87 | }
88 |
89 | /// Returns the current routing object set to execute its
90 | /// function on the specified queue
91 | public func on(_ queue: DispatchQueue) -> __BriskRoutingObj {
92 | self.opQueue = queue
93 | return self
94 | }
95 |
96 |
97 |
98 | // ----------- Execution -------------
99 |
100 |
101 | /// The sync property returns a function with the same input/output
102 | /// parameters of the original function. It is executed asynchronously
103 | /// on the specified queue. The calling thread is blocked until the
104 | /// called function completes. Not compatible with functions that throw
105 | /// errors.
106 | public var sync: (I) -> O {
107 | guard let opQ = opQueue else {
108 | brisk_raise("You must specify a queue for this function to operate on")
109 | }
110 |
111 | // If we're synchronous on the main thread already, just run the function immediately.
112 | if opQ === mainQueue && Thread.current.isMainThread {
113 | return { i in
114 | return self.wrappedFunction(i)
115 | }
116 | }
117 |
118 | guard !synchronized(lock, block: { let o = self.operated; self.operated = false; return o }) else {
119 | brisk_raise("You may not retain or use this routing object in a way that it can be executed more than once.")
120 | }
121 |
122 | return { i in
123 | self.dispatchGroup.enter()
124 | opQ.async {
125 | self.response = self.wrappedFunction(i)
126 | self.dispatchGroup.leave()
127 | }
128 | _ = self.dispatchGroup.wait(timeout: DispatchTime.distantFuture)
129 | return self.response! // Will be set in the async call above
130 | }
131 | }
132 |
133 |
134 | /// Processes the async handler applied to this routing object.
135 | fileprivate func processAsyncHandler(_ handler: @escaping (O) -> Void) {
136 | guard let hQ = self.handlerQueue else {
137 | brisk_raise("The handler queue was not specified before routing the async response")
138 | }
139 |
140 | backgroundQueue.async {
141 | _ = self.dispatchGroup.wait(timeout: DispatchTime.distantFuture)
142 | hQ.async {
143 | handler(self.response!) // Will be set in the async call before wait completes
144 | }
145 | }
146 | }
147 | }
148 |
149 |
150 | public final class __BriskRoutingObjVoid: __BriskRoutingObj {
151 |
152 | // Instantiate ourselves with a function
153 | override public init(function: @escaping (I) -> Void, defaultOpQueue: DispatchQueue? = nil) {
154 | super.init(function: function, defaultOpQueue: defaultOpQueue)
155 | }
156 |
157 | /// The async property returns a function that takes the parameters
158 | /// from the original function, executes the function with those
159 | /// parameters in the desired queue, then returns Void back to the
160 | /// originating thread. (for functions that originally return Void)
161 | ///
162 | /// When calling the wrapped function, the internal dispatchQueue
163 | /// is not exited until the wrapped function completes. This
164 | /// internal dispatchQueue can be waited on to funnel the response
165 | /// of the wrapped function to yet another async dispatch.
166 | public var async: (I) -> Void {
167 | guard let opQ = opQueue else {
168 | brisk_raise("You must specify a queue for this function to operate on")
169 | }
170 |
171 | guard !synchronized(lock, block: { let o = self.operated; self.operated = false; return o }) else {
172 | brisk_raise("You may not retain or use this routing object in a way that it can be executed more than once.")
173 | }
174 |
175 | return { i in
176 | self.dispatchGroup.enter()
177 | opQ.async {
178 | self.response = self.wrappedFunction(i)
179 | self.dispatchGroup.leave()
180 | }
181 | }
182 | }
183 | }
184 |
185 | public final class __BriskRoutingObjNonVoid: __BriskRoutingObj {
186 |
187 | // Instantiate ourselves with a function
188 | override public init(function: @escaping (I) -> O, defaultOpQueue: DispatchQueue? = nil) {
189 | super.init(function: function, defaultOpQueue: defaultOpQueue)
190 | }
191 |
192 | /// The async property returns a function that takes the parameters
193 | /// from the original function, executes the function with those
194 | /// parameters in the desired queue, then returns the original
195 | /// routing object back to the originating thread.
196 | ///
197 | /// When calling the wrapped function, the internal dispatchQueue
198 | /// is not exited until the wrapped function completes. This
199 | /// internal dispatchQueue can be waited on to funnel the response
200 | /// of the wrapped function to yet another async dispatch.
201 | public var async: (I) -> __BriskRoutingObjNonVoid {
202 | guard let opQ = opQueue else {
203 | brisk_raise("You must specify a queue for this function to operate on")
204 | }
205 |
206 | guard !synchronized(lock, block: { let o = self.operated; self.operated = false; return o }) else {
207 | brisk_raise("You may not retain or use this routing object in a way that it can be executed more than once.")
208 | }
209 |
210 | return { i in
211 | self.dispatchGroup.enter()
212 | opQ.async {
213 | self.response = self.wrappedFunction(i)
214 | self.dispatchGroup.leave()
215 | }
216 | return self
217 | }
218 | }
219 | }
220 |
221 |
222 |
223 | // MARK: - Operators
224 |
225 | postfix operator ->>
226 | postfix operator ~>>
227 | postfix operator +>>
228 |
229 | //postfix operator ?->>
230 | //postfix operator ?~>>
231 | //postfix operator ?+>>
232 |
233 |
234 | /// The ```->>``` postfix operator generates an internal routing object that
235 | /// requires you to specify the operation queue. An example of this
236 | /// would be:
237 | ///
238 | /// ```handler->>.main.async(result: nil)```
239 | @inline(__always) public postfix func ->>(function: @escaping (I) -> Void) -> __BriskRoutingObjVoid {
240 | return __BriskRoutingObjVoid(function: function)
241 | }
242 |
243 | /// The ```->>``` postfix operator generates an internal routing object that
244 | /// requires you to specify the operation queue. An example of this
245 | /// would be:
246 | ///
247 | /// ```handler->>.main.async(result: nil)```
248 | @inline(__always) public postfix func ->>(function: @escaping (I) -> O) -> __BriskRoutingObjNonVoid {
249 | return __BriskRoutingObjNonVoid(function: function)
250 | }
251 |
252 | /// The ```->>``` postfix operator generates an internal routing object that
253 | /// requires you to specify the operation queue. An example of this
254 | /// would be:
255 | ///
256 | /// ```handler->>.main.async(result: nil)```
257 | //@inline(__always) public postfix func ?->>(function: ((I) -> Void)?) -> __BriskRoutingObjVoid? {
258 | // return (function == nil) ? nil : __BriskRoutingObjVoid(function: function!)
259 | //}
260 |
261 | /// The ```->>``` postfix operator generates an internal routing object that
262 | /// requires you to specify the operation queue. An example of this
263 | /// would be:
264 | ///
265 | /// ```handler->>.main.async(result: nil)```
266 | //@inline(__always) public postfix func ?->>(function: ((I) -> O)?) -> __BriskRoutingObjNonVoid? {
267 | // return (function == nil) ? nil : __BriskRoutingObjNonVoid(function: function!)
268 | //}
269 |
270 |
271 |
272 |
273 | /// The ```~>>``` postfix operator generates an internal routing object that
274 | /// defaults to the concurrent background queue. An example of this
275 | /// would be:
276 | ///
277 | /// ```handler~>>.async(result: nil)```
278 | public postfix func ~>>(function: @escaping (I) -> Void) -> __BriskRoutingObjVoid {
279 | return __BriskRoutingObjVoid(function: function, defaultOpQueue: backgroundQueue)
280 | }
281 |
282 | /// The ```~>>``` postfix operator generates an internal routing object that
283 | /// defaults to the concurrent background queue. An example of this
284 | /// would be:
285 | ///
286 | /// ```handler~>>.async(result: nil)```
287 | public postfix func ~>>(function: @escaping (I) -> O) -> __BriskRoutingObjNonVoid {
288 | return __BriskRoutingObjNonVoid(function: function, defaultOpQueue: backgroundQueue)
289 | }
290 |
291 | /// The ```~>>``` postfix operator generates an internal routing object that
292 | /// defaults to the concurrent background queue. An example of this
293 | /// would be:
294 | ///
295 | /// ```handler~>>.async(result: nil)```
296 | //@inline(__always) public postfix func ?~>>(function: ((I) -> Void)?) -> __BriskRoutingObjVoid? {
297 | // return (function == nil) ? nil : __BriskRoutingObjVoid(function: function!, defaultOpQueue: backgroundQueue)
298 | //}
299 |
300 | /// The ```~>>``` postfix operator generates an internal routing object that
301 | /// defaults to the concurrent background queue. An example of this
302 | /// would be:
303 | ///
304 | /// ```handler~>>.async(result: nil)```
305 | //@inline(__always) public postfix func ?~>>(function: ((I) -> O)?) -> __BriskRoutingObjNonVoid? {
306 | // return (function == nil) ? nil : __BriskRoutingObjNonVoid(function: function!, defaultOpQueue: backgroundQueue)
307 | //}
308 |
309 |
310 |
311 |
312 | /// The ```+>>``` postfix operator generates an internal routing object that
313 | /// defaults to the main queue. An example of this would be:
314 | ///
315 | /// ```handler+>>.async(result: nil)```
316 | public postfix func +>>(function: @escaping (I) -> Void) -> __BriskRoutingObjVoid {
317 | return __BriskRoutingObjVoid(function: function, defaultOpQueue: mainQueue)
318 | }
319 |
320 | /// The ```+>>``` postfix operator generates an internal routing object that
321 | /// defaults to the main queue. An example of this would be:
322 | ///
323 | /// ```handler+>>.async(result: nil)```
324 | public postfix func +>>(function: @escaping (I) -> O) -> __BriskRoutingObjNonVoid {
325 | return __BriskRoutingObjNonVoid(function: function, defaultOpQueue: mainQueue)
326 | }
327 |
328 | /// The ```+>>``` postfix operator generates an internal routing object that
329 | /// defaults to the main queue. An example of this would be:
330 | ///
331 | /// ```handler+>>.async(result: nil)```
332 | //@inline(__always) public postfix func ?+>>(function: ((I) -> Void)?) -> __BriskRoutingObjVoid? {
333 | // return (function == nil) ? nil : __BriskRoutingObjVoid(function: function!, defaultOpQueue: mainQueue)
334 | //}
335 |
336 | /// The ```+>>``` postfix operator generates an internal routing object that
337 | /// defaults to the main queue. An example of this would be:
338 | ///
339 | /// ```handler+>>.async(result: nil)```
340 | //@inline(__always) public postfix func ?+>>(function: ((I) -> O)?) -> __BriskRoutingObjNonVoid? {
341 | // return (function == nil) ? nil : __BriskRoutingObjNonVoid(function: function!, defaultOpQueue: mainQueue)
342 | //}
343 |
344 |
345 | /* -- old precendence = 140 -- */
346 | precedencegroup AsyncRedirectPrecendence {
347 | higherThan: RangeFormationPrecedence
348 | lowerThan: MultiplicationPrecedence
349 | associativity: left
350 | }
351 |
352 | infix operator +>> : AsyncRedirectPrecendence
353 | infix operator ~>> : AsyncRedirectPrecendence
354 | infix operator ?+>> : AsyncRedirectPrecendence
355 | infix operator ?~>> : AsyncRedirectPrecendence
356 |
357 |
358 | /// The ```~>>``` infix operator allows for shorthand creation of a routing object
359 | /// that operates asynchronously on the global concurrent background queue.
360 | ///
361 | /// - e.g.: ```handler~>>(param: nil)```
362 | public func ~>>(lhs: @escaping (I) -> Void, rhs: I) -> Void {
363 | return __BriskRoutingObjVoid(function: lhs, defaultOpQueue: backgroundQueue).async(rhs)
364 | }
365 |
366 | /// The ```~>>``` infix operator allows for shorthand creation of a routing object
367 | /// that operates asynchronously on the global concurrent background queue.
368 | ///
369 | /// - e.g.: ```handler~>>(param: nil)```
370 | @discardableResult public func ~>>(lhs: @escaping (I) -> O, rhs: I) -> __BriskRoutingObjNonVoid {
371 | return __BriskRoutingObjNonVoid(function: lhs, defaultOpQueue: backgroundQueue).async(rhs)
372 | }
373 |
374 | /// The ```~>>``` infix operator allows for shorthand execution of the wrapped function
375 | /// on its defined operation queue.
376 | ///
377 | /// - e.g.: ```handler~>>(param: nil)```
378 | public func ~>>(lhs: __BriskRoutingObjVoid, rhs: I) -> Void {
379 | return lhs.async(rhs)
380 | }
381 |
382 | /// The ```~>>``` infix operator allows for shorthand execution of the wrapped function
383 | /// on its defined operation queue.
384 | ///
385 | /// - e.g.: ```handler~>>(param: nil)```
386 | @discardableResult public func ~>>(lhs: __BriskRoutingObjNonVoid, rhs: I) -> __BriskRoutingObjNonVoid {
387 | return lhs.async(rhs)
388 | }
389 |
390 |
391 |
392 | /// The ```~>>``` infix operator allows for shorthand creation of a routing object
393 | /// that operates asynchronously on the global concurrent background queue.
394 | ///
395 | /// - e.g.: ```handler~>>(param: nil)```
396 | public func ?~>>(lhs: ((I) -> Void)?, rhs: I) -> Void {
397 | if let lhs = lhs { __BriskRoutingObjVoid(function: lhs, defaultOpQueue: backgroundQueue).async(rhs) }
398 | }
399 |
400 | /// The ```~>>``` infix operator allows for shorthand creation of a routing object
401 | /// that operates asynchronously on the global concurrent background queue.
402 | ///
403 | /// - e.g.: ```handler~>>(param: nil)```
404 | @discardableResult public func ?~>>(lhs: ((I) -> O)?, rhs: I) -> __BriskRoutingObjNonVoid? {
405 | return (lhs == nil) ? nil : __BriskRoutingObjNonVoid(function: lhs!, defaultOpQueue: backgroundQueue).async(rhs)
406 | }
407 |
408 | /// The ```~>>``` infix operator allows for shorthand execution of the wrapped function
409 | /// on its defined operation queue.
410 | ///
411 | /// - e.g.: ```handler~>>(param: nil)```
412 | public func ?~>>(lhs: __BriskRoutingObjVoid?, rhs: I) -> Void {
413 | lhs?.async(rhs)
414 | }
415 |
416 | /// The ```~>>``` infix operator allows for shorthand execution of the wrapped function
417 | /// on its defined operation queue.
418 | ///
419 | /// - e.g.: ```handler~>>(param: nil)```
420 | @discardableResult public func ?~>>(lhs: __BriskRoutingObjNonVoid?, rhs: I) -> __BriskRoutingObjNonVoid? {
421 | return lhs?.async(rhs)
422 | }
423 |
424 |
425 |
426 |
427 |
428 | /// The ```+>>``` infix operator allows for shorthand creation of a routing object
429 | /// that operates asynchronously on the main queue.
430 | ///
431 | /// - e.g.: ```handler+>>(param: nil)```
432 | public func +>>(lhs: @escaping (I) -> Void, rhs: I) -> Void {
433 | return __BriskRoutingObjVoid(function: lhs, defaultOpQueue: mainQueue).async(rhs)
434 | }
435 |
436 | /// The ```+>>``` infix operator allows for shorthand creation of a routing object
437 | /// that operates asynchronously on the main queue.
438 | ///
439 | /// - e.g.: ```handler+>>(param: nil)```
440 | @discardableResult public func +>>(lhs: @escaping (I) -> O, rhs: I) -> __BriskRoutingObjNonVoid {
441 | return __BriskRoutingObjNonVoid(function: lhs, defaultOpQueue: mainQueue).async(rhs)
442 | }
443 |
444 | /// The ```+>>``` infix operator allows for shorthand creation of a routing object
445 | /// that operates asynchronously on the main queue.
446 | ///
447 | /// - e.g.: ```handler+>>(param: nil)```
448 | public func ?+>>(lhs: ((I) -> Void)?, rhs: I) -> Void {
449 | if let lhs = lhs { __BriskRoutingObjVoid(function: lhs, defaultOpQueue: mainQueue).async(rhs) }
450 | }
451 |
452 | /// The ```+>>``` infix operator allows for shorthand creation of a routing object
453 | /// that operates asynchronously on the main queue.
454 | ///
455 | /// - e.g.: ```handler+>>(param: nil)```
456 | @discardableResult public func ?+>>(lhs: ((I) -> O)?, rhs: I) -> __BriskRoutingObjNonVoid? {
457 | return (lhs == nil) ? nil : __BriskRoutingObjNonVoid(function: lhs!, defaultOpQueue: mainQueue).async(rhs)
458 | }
459 |
460 |
461 |
462 |
463 |
464 | /// The special ```~>>``` infix operator between a function and a queue creates a
465 | /// routing object that will call its operation on that queue.
466 | ///
467 | /// - e.g.: ```handler +>> (param: nil) ~>> myQueue ~>> { result in ... }```
468 | /// - e.g.: ```handler ~>> myQueue ~>> (param: nil) ~>> myOtherQueue ~>> { result in ... }```
469 | public func ~>>(lhs: @escaping (I) -> Void, rhs: DispatchQueue) -> __BriskRoutingObjVoid {
470 | return __BriskRoutingObjVoid(function: lhs, defaultOpQueue: rhs)
471 | }
472 |
473 | /// The special ```~>>``` infix operator between a function and a queue creates a
474 | /// routing object that will call its operation on that queue.
475 | ///
476 | /// - e.g.: ```handler +>> (param: nil) ~>> myQueue ~>> { result in ... }```
477 | /// - e.g.: ```handler ~>> myQueue ~>> (param: nil) ~>> myOtherQueue ~>> { result in ... }```
478 | public func ~>>(lhs: @escaping (I) -> O, rhs: DispatchQueue) -> __BriskRoutingObjNonVoid {
479 | return __BriskRoutingObjNonVoid(function: lhs, defaultOpQueue: rhs)
480 | }
481 |
482 | /// The special ```~>>``` infix operator allows you to specify the queues for the
483 | /// routing operations. This sets the initial operation queue if it hasn't already
484 | /// been defined by on(). If the initial operation queue has already been defined,
485 | /// this sets the response handler queue.
486 | ///
487 | /// - e.g.: ```handler +>> (param: nil) ~>> myQueue ~>> { result in ... }```
488 | /// - e.g.: ```handler ~>> myQueue ~>> (param: nil) ~>> myOtherQueue ~>> { result in ... }```
489 | public func ~>>(lhs: __BriskRoutingObjNonVoid, rhs: DispatchQueue) -> __BriskRoutingObjNonVoid {
490 | lhs.handlerQueue = rhs
491 | return lhs
492 | }
493 |
494 | /// The special ```~>>``` infix operator between a function and a queue creates a
495 | /// routing object that will call its operation on that queue.
496 | ///
497 | /// - e.g.: ```handler +>> (param: nil) ~>> myQueue ~>> { result in ... }```
498 | /// - e.g.: ```handler ~>> myQueue ~>> (param: nil) ~>> myOtherQueue ~>> { result in ... }```
499 | public func ?~>>(lhs: ((I) -> Void)?, rhs: DispatchQueue) -> __BriskRoutingObjVoid? {
500 | return (lhs == nil) ? nil : __BriskRoutingObjVoid(function: lhs!, defaultOpQueue: rhs)
501 | }
502 |
503 | /// The special ```~>>``` infix operator between a function and a queue creates a
504 | /// routing object that will call its operation on that queue.
505 | ///
506 | /// - e.g.: ```handler +>> (param: nil) ~>> myQueue ~>> { result in ... }```
507 | /// - e.g.: ```handler ~>> myQueue ~>> (param: nil) ~>> myOtherQueue ~>> { result in ... }```
508 | public func ?~>>(lhs: ((I) -> O)?, rhs: DispatchQueue) -> __BriskRoutingObjNonVoid? {
509 | return (lhs == nil) ? nil : __BriskRoutingObjNonVoid(function: lhs!, defaultOpQueue: rhs)
510 | }
511 |
512 | /// The special ```~>>``` infix operator allows you to specify the queues for the
513 | /// routing operations. This sets the initial operation queue if it hasn't already
514 | /// been defined by on(). If the initial operation queue has already been defined,
515 | /// this sets the response handler queue.
516 | ///
517 | /// - e.g.: ```handler +>> (param: nil) ~>> myQueue ~>> { result in ... }```
518 | /// - e.g.: ```handler ~>> myQueue ~>> (param: nil) ~>> myOtherQueue ~>> { result in ... }```
519 | public func ~>>(lhs: __BriskRoutingObjNonVoid?, rhs: DispatchQueue) -> __BriskRoutingObjNonVoid? {
520 | lhs?.handlerQueue = rhs
521 | return lhs
522 | }
523 |
524 |
525 |
526 |
527 | /// The ```~>>``` infix operator routes the result of your asynchronous operation
528 | /// to a completion handler that is executed on the predefined queue, or the global
529 | /// concurrent background queue by default if none was specified.
530 | ///
531 | /// -e.g.: ```handler~>>(param: nil) ~>> { result in ... }```
532 | public func ~>>(lhs: __BriskRoutingObjNonVoid, rhs: @escaping (O) -> Void) {
533 | if lhs.handlerQueue == nil { lhs.handlerQueue = backgroundQueue }
534 | lhs.processAsyncHandler(rhs)
535 | }
536 |
537 | /// The ```+>>``` infix operator routes the result of your asynchronous operation
538 | /// to a completion handler that is executed on the main queue.
539 | ///
540 | /// -e.g.: ```handler~>>(param: nil) +>> { result in ... }```
541 | public func +>>(lhs: __BriskRoutingObjNonVoid, rhs: @escaping (O) -> Void) {
542 | lhs.handlerQueue = mainQueue
543 | lhs.processAsyncHandler(rhs)
544 | }
545 |
546 | /// The ```~>>``` infix operator routes the result of your asynchronous operation
547 | /// to a completion handler that is executed on the predefined queue, or the global
548 | /// concurrent background queue by default if none was specified.
549 | ///
550 | /// -e.g.: ```handler~>>(param: nil) ~>> { result in ... }```
551 | public func ~>>(lhs: __BriskRoutingObjNonVoid?, rhs: @escaping (O) -> Void) {
552 | if let lhs = lhs {
553 | if lhs.handlerQueue == nil { lhs.handlerQueue = backgroundQueue }
554 | lhs.processAsyncHandler(rhs)
555 | }
556 | }
557 |
558 | /// The ```+>>``` infix operator routes the result of your asynchronous operation
559 | /// to a completion handler that is executed on the main queue.
560 | ///
561 | /// -e.g.: ```handler~>>(param: nil) +>> { result in ... }```
562 | public func +>>(lhs: __BriskRoutingObjNonVoid?, rhs: @escaping (O) -> Void) {
563 | lhs?.handlerQueue = mainQueue
564 | lhs?.processAsyncHandler(rhs)
565 | }
566 |
567 |
568 |
569 |
570 |
571 |
572 |
--------------------------------------------------------------------------------
/BriskTests/BriskTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BriskTests.swift
3 | // BriskTests
4 | //
5 | // Copyright (c) 2016-Present Jason Fieldman - https://github.com/jmfieldman/Brisk
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import XCTest
27 | @testable import Brisk
28 |
29 |
30 | private let mainQueueKey = DispatchSpecificKey()
31 | private let mainQueueValue: Int = 31337
32 |
33 | private func onMainQueue() -> Bool {
34 | return DispatchQueue.getSpecific(key: mainQueueKey) == mainQueueValue
35 | }
36 |
37 | private func onMainThread() -> Bool {
38 | return Thread.current.isMainThread
39 | }
40 |
41 | private func onMainEverything() -> Bool {
42 | return onMainThread() && onMainQueue()
43 | }
44 |
45 | class BriskTests: XCTestCase {
46 |
47 |
48 |
49 | override func setUp() {
50 | super.setUp()
51 | // Put setup code here. This method is called before the invocation of each test method in the class.
52 |
53 | DispatchQueue.main.setSpecific(key: /*Migrator FIXME: Use a variable of type DispatchSpecificKey*/ mainQueueKey,
54 | value: mainQueueValue)
55 | }
56 |
57 | override func tearDown() {
58 | // Put teardown code here. This method is called after the invocation of each test method in the class.
59 | super.tearDown()
60 | }
61 |
62 |
63 | // MARK: - Swift 2.x dispatch stuff
64 |
65 | func testBasicSwift2xDispatchMain() {
66 | let spin = MainSpin()
67 | var qPassed = false
68 |
69 | spin.start()
70 | dispatch_main_async {
71 | qPassed = onMainEverything()
72 | spin.done()
73 | }
74 | spin.wait()
75 |
76 | XCTAssertTrue(qPassed, "on incorrect queue")
77 | }
78 |
79 |
80 | func testBasicSwift2xDispatchAsync() {
81 | let spin = AsyncSpin()
82 | var qPassed = false
83 |
84 | spin.start()
85 | dispatch_bg_async {
86 | qPassed = !onMainQueue()
87 | spin.done()
88 | }
89 | spin.wait()
90 |
91 | XCTAssertTrue(qPassed, "on incorrect queue")
92 | }
93 |
94 |
95 | func testBasicSwift2xDispatchMainAfter() {
96 |
97 | let spin = MainSpin()
98 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
99 | var time2: CFAbsoluteTime = 0
100 | var qPassed = false
101 |
102 | spin.start()
103 | dispatch_main_after(0.5) {
104 | time2 = CFAbsoluteTimeGetCurrent()
105 | qPassed = onMainEverything()
106 | spin.done()
107 | }
108 | spin.wait()
109 |
110 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.25")
111 | XCTAssertTrue(qPassed, "on incorrect queue")
112 | }
113 |
114 | func testBasicSwift2xDispatchAsyncAfter() {
115 |
116 | let spin = AsyncSpin()
117 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
118 | var time2: CFAbsoluteTime = 0
119 | var qPassed = false
120 |
121 | spin.start()
122 | dispatch_after(0.5, backgroundQueue) {
123 | time2 = CFAbsoluteTimeGetCurrent()
124 | qPassed = !onMainQueue()
125 | spin.done()
126 | }
127 | spin.wait()
128 |
129 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.5")
130 | XCTAssertTrue(qPassed, "on incorrect queue")
131 | }
132 |
133 |
134 | func testBasicSwift2xDispatchMainOnce() {
135 |
136 | let spin = MainSpin()
137 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
138 | var time2: CFAbsoluteTime = 0
139 | var qPassed = false
140 | var count = 0
141 |
142 | spin.start()
143 | let block = {
144 | time2 = CFAbsoluteTimeGetCurrent()
145 | count += 1
146 | qPassed = onMainEverything()
147 | spin.done()
148 | }
149 | for _ in 0 ..< 10 {
150 | dispatch_main_once_after(0.5, operationId: "testop2") {
151 | block()
152 | }
153 | }
154 | spin.wait()
155 |
156 | XCTAssertEqual(count, 1, "counted more than once")
157 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.5")
158 | XCTAssertTrue(qPassed, "on incorrect queue")
159 |
160 | }
161 |
162 |
163 | func testBasicSwift2xDispatchAsyncOnce() {
164 |
165 | let spin = AsyncSpin()
166 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
167 | var time2: CFAbsoluteTime = 0
168 | var qPassed = false
169 | var count = 0
170 |
171 | spin.start()
172 | let block = {
173 | time2 = CFAbsoluteTimeGetCurrent()
174 | count += 1
175 | qPassed = !onMainQueue()
176 | spin.done()
177 | }
178 | for _ in 0 ..< 10 {
179 | dispatch_once_after(0.5, operationId: "testop", onQueue: backgroundQueue) {
180 | block()
181 | }
182 | }
183 | spin.wait()
184 |
185 | XCTAssertEqual(count, 1, "counted more than once")
186 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.5")
187 | XCTAssertTrue(qPassed, "on incorrect queue")
188 |
189 | }
190 |
191 |
192 | func testBasicSwift2xDispatchEveryMain() {
193 |
194 | let spin = MainSpin()
195 | var count = 0
196 | var qPassed = true
197 |
198 | spin.start()
199 | dispatch_main_every(0.1) { t in
200 | count += 1
201 | if !onMainEverything() { qPassed = false }
202 | if count == 10 {
203 | spin.done()
204 | t.cancel()
205 | }
206 | }
207 | spin.wait()
208 |
209 | XCTAssertEqual(count, 10, "counted incorrect times")
210 | XCTAssertTrue(qPassed, "on incorrect queue")
211 | }
212 |
213 | func testBasicSwift2xDispatchEveryMainExact() {
214 |
215 | let spin = MainSpin()
216 | var count = 0
217 | var qPassed = true
218 |
219 | spin.start()
220 | dispatch_main_every_exact(0.1) { t in
221 | count += 1
222 | if !onMainEverything() { qPassed = false }
223 | if count == 10 {
224 | spin.done()
225 | t.cancel()
226 | }
227 | }
228 | spin.wait()
229 |
230 | XCTAssertEqual(count, 10, "counted incorrect times")
231 | XCTAssertTrue(qPassed, "on incorrect queue")
232 | }
233 |
234 | func testBasicSwift2xDispatchEveryAsync() {
235 |
236 | let spin = AsyncSpin()
237 | var count = 0
238 | var qPassed = true
239 |
240 | spin.start()
241 | dispatch_every(0.1, backgroundQueue) { t in
242 | count += 1
243 | if onMainQueue() { qPassed = false }
244 | if count == 10 {
245 | spin.done()
246 | t.cancel()
247 | }
248 | }
249 | spin.wait()
250 |
251 | XCTAssertEqual(count, 10, "counted incorrect times")
252 | XCTAssertTrue(qPassed, "on incorrect queue")
253 | }
254 |
255 |
256 | func testBasicSwift2xDispatchEveryAsyncExact() {
257 |
258 | let spin = AsyncSpin()
259 | var count = 0
260 | var qPassed = true
261 |
262 | spin.start()
263 | dispatch_every_exact(0.1, backgroundQueue) { t in
264 | count += 1
265 | if onMainQueue() { qPassed = false }
266 | if count == 10 {
267 | spin.done()
268 | t.cancel()
269 | }
270 | }
271 | spin.wait()
272 |
273 | XCTAssertEqual(count, 10, "counted incorrect times")
274 | XCTAssertTrue(qPassed, "on incorrect queue")
275 | }
276 |
277 |
278 | // MARK: - Swift 3.x dispatch stuff
279 |
280 |
281 | func testBasicSwift3xDispatchMainAfter() {
282 |
283 | let spin = MainSpin()
284 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
285 | var time2: CFAbsoluteTime = 0
286 | var qPassed = false
287 |
288 | spin.start()
289 | DispatchQueue.main.async(after: 0.5) {
290 | time2 = CFAbsoluteTimeGetCurrent()
291 | qPassed = onMainEverything()
292 | spin.done()
293 | }
294 | spin.wait()
295 |
296 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.25")
297 | XCTAssertTrue(qPassed, "on incorrect queue")
298 | }
299 |
300 | func testBasicSwift3xDispatchAsyncAfter() {
301 |
302 | let spin = AsyncSpin()
303 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
304 | var time2: CFAbsoluteTime = 0
305 | var qPassed = false
306 |
307 | spin.start()
308 | backgroundQueue.async(after: 0.5) {
309 | time2 = CFAbsoluteTimeGetCurrent()
310 | qPassed = !onMainQueue()
311 | spin.done()
312 | }
313 | spin.wait()
314 |
315 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.5")
316 | XCTAssertTrue(qPassed, "on incorrect queue")
317 | }
318 |
319 |
320 | func testBasicSwift3xDispatchMainOnce() {
321 |
322 | let spin = MainSpin()
323 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
324 | var time2: CFAbsoluteTime = 0
325 | var qPassed = false
326 | var count = 0
327 |
328 | spin.start()
329 | let block = {
330 | time2 = CFAbsoluteTimeGetCurrent()
331 | count += 1
332 | qPassed = onMainEverything()
333 | spin.done()
334 | }
335 | for _ in 0 ..< 10 {
336 | DispatchQueue.main.once(operationId: "testop2", after: 0.5) {
337 | block()
338 | }
339 | }
340 | spin.wait()
341 |
342 | XCTAssertEqual(count, 1, "counted more than once")
343 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.5")
344 | XCTAssertTrue(qPassed, "on incorrect queue")
345 |
346 | }
347 |
348 |
349 | func testBasicSwift3xDispatchAsyncOnce() {
350 |
351 | let spin = AsyncSpin()
352 | let time1: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
353 | var time2: CFAbsoluteTime = 0
354 | var qPassed = false
355 | var count = 0
356 |
357 | spin.start()
358 | let block = {
359 | time2 = CFAbsoluteTimeGetCurrent()
360 | count += 1
361 | qPassed = !onMainQueue()
362 | spin.done()
363 | }
364 | for _ in 0 ..< 10 {
365 | backgroundQueue.once(operationId: "testop", after: 0.5) {
366 | block()
367 | }
368 | }
369 | spin.wait()
370 |
371 | XCTAssertEqual(count, 1, "counted more than once")
372 | XCTAssertGreaterThan(time2 - time1, 0.25, "diff must be greater than 0.5")
373 | XCTAssertTrue(qPassed, "on incorrect queue")
374 |
375 | }
376 |
377 |
378 | func testBasicSwift3xDispatchEveryMain() {
379 |
380 | let spin = MainSpin()
381 | var count = 0
382 | var qPassed = true
383 |
384 | spin.start()
385 | DispatchQueue.main.async(every: 0.1) { (t: DispatchSourceTimer) in
386 | count += 1
387 | if !onMainEverything() { qPassed = false }
388 | if count == 10 {
389 | spin.done()
390 | //t.cancel()
391 | }
392 | }
393 | spin.wait()
394 |
395 | XCTAssertEqual(count, 10, "counted incorrect times")
396 | XCTAssertTrue(qPassed, "on incorrect queue")
397 | }
398 |
399 | func testBasicSwift3xDispatchEveryMainExact() {
400 |
401 | let spin = MainSpin()
402 | var count = 0
403 | var qPassed = true
404 |
405 | spin.start()
406 | DispatchQueue.main.async(every: 0.1, leeway: 0) { (t: DispatchSourceTimer) in
407 | count += 1
408 | if !onMainEverything() { qPassed = false }
409 | if count == 10 {
410 | spin.done()
411 | t.cancel()
412 | }
413 | }
414 | spin.wait()
415 |
416 | XCTAssertEqual(count, 10, "counted incorrect times")
417 | XCTAssertTrue(qPassed, "on incorrect queue")
418 | }
419 |
420 | func testBasicSwift3xDispatchEveryAsync() {
421 |
422 | let spin = AsyncSpin()
423 | var count = 0
424 | var qPassed = true
425 |
426 | spin.start()
427 | backgroundQueue.async(every: 0.1) { (t: DispatchSourceTimer) in
428 | count += 1
429 | if onMainQueue() { qPassed = false }
430 | if count == 10 {
431 | spin.done()
432 | t.cancel()
433 | }
434 | }
435 | spin.wait()
436 |
437 | XCTAssertEqual(count, 10, "counted incorrect times")
438 | XCTAssertTrue(qPassed, "on incorrect queue")
439 | }
440 |
441 |
442 | func testBasicSwift3xDispatchEveryAsyncExact() {
443 |
444 | let spin = AsyncSpin()
445 | var count = 0
446 | var qPassed = true
447 |
448 | spin.start()
449 | backgroundQueue.async(every: 0.1, leeway: 0) { (t: DispatchSourceTimer) in
450 | count += 1
451 | if onMainQueue() { qPassed = false }
452 | if count == 10 {
453 | spin.done()
454 | t.cancel()
455 | }
456 | }
457 | spin.wait()
458 |
459 | XCTAssertEqual(count, 10, "counted incorrect times")
460 | XCTAssertTrue(qPassed, "on incorrect queue")
461 | }
462 |
463 |
464 |
465 | // MARK: - Async To Sync
466 |
467 |
468 | func testBasicAsync2SyncConceptBG() {
469 | let spin = MainSpin()
470 | spin.start()
471 |
472 | var res: Int = 0
473 |
474 | backgroundQueue.async {
475 | res = <<~{ self.asyncTest_CallsOnMainReturns4($0) }
476 | spin.done()
477 | }
478 |
479 | spin.wait()
480 |
481 | XCTAssertEqual(res, 4, "incorrect response")
482 | }
483 |
484 |
485 | func testBasicAsync2SyncConceptMain() {
486 | let spin = MainSpin()
487 | spin.start()
488 |
489 | var res: Int = 0
490 |
491 | backgroundQueue.async {
492 | res = <<+{ self.asyncTest_CallsOnMainReturns4($0) }
493 | spin.done()
494 | }
495 |
496 | spin.wait()
497 |
498 | XCTAssertEqual(res, 4, "incorrect response")
499 | }
500 |
501 | func testBasicAsync2SyncConceptBG2() {
502 | let spin = MainSpin()
503 | spin.start()
504 |
505 | var res: Int = 0
506 |
507 | backgroundQueue.async {
508 | res = <<~{ self.asyncTest_CallsOnMainReturnsI(3, handler: $0) }
509 | spin.done()
510 | }
511 |
512 | spin.wait()
513 |
514 | XCTAssertEqual(res, 3, "incorrect response")
515 | }
516 |
517 | func testBasicAsync2SyncConceptBG3() {
518 | let spin = MainSpin()
519 | spin.start()
520 |
521 | var res: Int = 0
522 | var str: String = ""
523 |
524 | backgroundQueue.async {
525 | (res, str) = <<~{ self.asyncTest_CallsOnMainReturnsIforBoth(3, handler: $0) }
526 | spin.done()
527 | }
528 |
529 | spin.wait()
530 |
531 | XCTAssertEqual(res, 3, "incorrect response int")
532 | XCTAssertEqual(str, "3", "incorrect response str")
533 | }
534 |
535 | func testBasicAsync2SyncConceptBG3_ImmediateOp() {
536 | let spin = MainSpin()
537 | spin.start()
538 |
539 | var res: Int = 0
540 | var str: String = ""
541 |
542 | backgroundQueue.async {
543 | (res, str) = <<-{ self.asyncTest_CallsOnMainReturnsIforBoth(3, handler: $0) }
544 | spin.done()
545 | }
546 |
547 | spin.wait()
548 |
549 | XCTAssertEqual(res, 3, "incorrect response int")
550 | XCTAssertEqual(str, "3", "incorrect response str")
551 | }
552 |
553 |
554 | func testBasicAsync2SyncConceptBG3_QueueOp() {
555 | let spin = MainSpin()
556 | spin.start()
557 |
558 | var res: Int = 0
559 | var str: String = ""
560 |
561 | let myQueue = DispatchQueue(label: "test", attributes: [])
562 |
563 | backgroundQueue.async {
564 | (res, str) = <<~myQueue ~~~ { self.asyncTest_CallsOnMainReturnsIforBoth(3, handler: $0) }
565 | spin.done()
566 | }
567 |
568 | spin.wait()
569 |
570 | XCTAssertEqual(res, 3, "incorrect response int")
571 | XCTAssertEqual(str, "3", "incorrect response str")
572 | }
573 |
574 |
575 | func testBasicAsync2SyncConceptONMain() {
576 | let (res, str) = <<-{ asyncTest_CallsOnMainReturnsIforBoth(3, handler: $0) }
577 |
578 | XCTAssertEqual(res, 3, "incorrect response int")
579 | XCTAssertEqual(str, "3", "incorrect response str")
580 | }
581 |
582 | func testBasicAsync2SyncConceptONMain2() {
583 | let (res, str) = <<+{ self.asyncTest_CallsOnMainReturnsIforBoth(3, handler: $0) }
584 |
585 | XCTAssertEqual(res, 3, "incorrect response int")
586 | XCTAssertEqual(str, "3", "incorrect response str")
587 | }
588 |
589 | func testBasicAsync2SyncConceptONMain3() {
590 | let (res, str) = <<~{ self.asyncTest_CallsOnMainReturnsIforBoth(3, handler: $0) }
591 |
592 | XCTAssertEqual(res, 3, "incorrect response int")
593 | XCTAssertEqual(str, "3", "incorrect response str")
594 | }
595 |
596 |
597 | /*
598 | func testOptionalReturn() {
599 | { [weak self] in
600 | // This will fatal error
601 | let (res, str) = <<~{ self?.asyncTest_CallsOnMainReturnsIforBoth(3, handler: $0) }
602 |
603 | XCTAssertEqual(res, 3, "incorrect response int")
604 | XCTAssertEqual(str, "3", "incorrect response str")
605 | }()
606 | }*/
607 |
608 |
609 | // MARK: - Sync To Async
610 |
611 | func testSync2Async_MainA() {
612 | let spin = MainSpin()
613 | spin.start()
614 | var r: Int = 0
615 | var inMain = false
616 |
617 | syncTest_Return4+>>() +>> { inMain = Thread.current.isMainThread; r = $0; spin.done() }
618 | spin.wait()
619 |
620 | XCTAssertEqual(r, 4, "incorrect response int")
621 | XCTAssertEqual(inMain, true, "incorrect queue")
622 | }
623 |
624 | func testSync2Async_MainB() {
625 | let spin = MainSpin()
626 | spin.start()
627 | var r: Int = 0
628 | var inMain = false
629 |
630 | syncTest_Return4 +>> () +>> { inMain = Thread.current.isMainThread; r = $0; spin.done() }
631 | spin.wait()
632 |
633 | XCTAssertEqual(r, 4, "incorrect response int")
634 | XCTAssertEqual(inMain, true, "incorrect queue")
635 | }
636 |
637 | func testSync2Async_MainC() {
638 | let spin = MainSpin()
639 | spin.start()
640 | var r: Int = 0
641 | var inMain = false
642 |
643 | syncTest_ReturnsI+>>.async(4) +>> { inMain = Thread.current.isMainThread; r = $0; spin.done() }
644 | spin.wait()
645 |
646 | XCTAssertEqual(r, 4, "incorrect response int")
647 | XCTAssertEqual(inMain, true, "incorrect queue")
648 | }
649 |
650 | func testSync2Async_MainD() {
651 | let spin = MainSpin()
652 | spin.start()
653 | var r: Int = 0
654 | var inMain = false
655 |
656 | syncTest_ReturnsI +>> (4) +>> { inMain = Thread.current.isMainThread; r = $0; spin.done() }
657 | spin.wait()
658 |
659 | XCTAssertEqual(r, 4, "incorrect response int")
660 | XCTAssertEqual(inMain, true, "incorrect queue")
661 | }
662 |
663 | func testSync2Async_MainE() {
664 | let spin = MainSpin()
665 | spin.start()
666 | var r: Int = 0
667 | var inMain = false
668 |
669 | syncTest_ReturnsI ~>> (4) +>> { inMain = Thread.current.isMainThread; r = $0; spin.done() }
670 | spin.wait()
671 |
672 | XCTAssertEqual(r, 4, "incorrect response int")
673 | XCTAssertEqual(inMain, true, "incorrect queue")
674 | }
675 |
676 | func testSync2Async_MainBothA() {
677 | let spin = MainSpin()
678 | spin.start()
679 | var r: Int = 0
680 | var s: String = ""
681 | var inMain = false
682 |
683 | syncTest_ReturnsIforBoth ~>> (4) +>> { inMain = onMainQueue(); (r, s) = $0; spin.done() }
684 | spin.wait()
685 |
686 | XCTAssertEqual(r, 4, "incorrect response int")
687 | XCTAssertEqual(s, "4", "incorrect response str")
688 | XCTAssertEqual(inMain, true, "incorrect queue")
689 | }
690 |
691 | func testSync2Async_MainBothB() {
692 | let spin = MainSpin()
693 | spin.start()
694 | var r: Int = 0
695 | var s: String = ""
696 | var inMain = false
697 |
698 | syncTest_ReturnsIforBoth ~>> (4) ~>> DispatchQueue.main ~>> { inMain = onMainQueue(); (r, s) = $0; spin.done() }
699 | spin.wait()
700 |
701 | XCTAssertEqual(r, 4, "incorrect response int")
702 | XCTAssertEqual(s, "4", "incorrect response str")
703 | XCTAssertEqual(inMain, true, "incorrect queue")
704 | }
705 |
706 | func testSync2Async_BGBothB() {
707 | let spin = MainSpin()
708 | spin.start()
709 | var r: Int = 0
710 | var s: String = ""
711 | var inMain = false
712 |
713 | syncTest_ReturnsIforBoth ~>> (4) ~>> DispatchQueue(label: "", attributes: []) ~>> { inMain = onMainQueue(); (r, s) = $0; spin.done() }
714 | spin.wait()
715 |
716 | XCTAssertEqual(r, 4, "incorrect response int")
717 | XCTAssertEqual(s, "4", "incorrect response str")
718 | XCTAssertEqual(inMain, false, "incorrect queue")
719 | }
720 |
721 | func testSync2Async_MainRetA() {
722 | var r: Int = 0
723 | var s: String = ""
724 |
725 | (r, s) = syncTest_ReturnsIforBoth~>>.sync(4)
726 |
727 | XCTAssertEqual(r, 4, "incorrect response int")
728 | XCTAssertEqual(s, "4", "incorrect response str")
729 | }
730 |
731 | func testSync2Async_MainRetB() {
732 | var r: Int = 0
733 | var s: String = ""
734 |
735 | (r, s) = syncTest_ReturnsIforBoth+>>.sync(4)
736 |
737 | XCTAssertEqual(r, 4, "incorrect response int")
738 | XCTAssertEqual(s, "4", "incorrect response str")
739 | }
740 |
741 | func testSync2Async_MainRetC() {
742 | var r: Int = 0
743 | var inMain = false
744 |
745 | r = { (i: Int) in inMain = onMainQueue(); return i }+>>.sync(4)
746 |
747 | XCTAssertEqual(r, 4, "incorrect response int")
748 | XCTAssertEqual(inMain, true, "incorrect queue")
749 | }
750 |
751 | func testSync2Async_MainRetD() {
752 | var r: Int = 0
753 | var inMain = true
754 |
755 | r = { (i: Int) in inMain = onMainQueue(); return i }~>>.sync(4)
756 |
757 | XCTAssertEqual(r, 4, "incorrect response int")
758 | XCTAssertEqual(inMain, false, "incorrect queue")
759 | }
760 |
761 | func testSync2Async_MainRetE() {
762 | var inMain = true;
763 |
764 | { inMain = onMainQueue() }~>>.sync()
765 |
766 | XCTAssertEqual(inMain, false, "incorrect queue")
767 | }
768 |
769 | func testSync2Async_MainRetF() {
770 | var inMain = false;
771 |
772 | { inMain = onMainQueue() }+>>.sync()
773 |
774 | XCTAssertEqual(inMain, true, "incorrect queue")
775 | }
776 |
777 | // Compile checks
778 |
779 | func makeSureThisCompiles() {
780 |
781 | syncTest_Return4+>>() +>> { i in }
782 | syncTest_ReturnsI~>>.async(3) +>> { i in }
783 | syncTest_Return4~>>()
784 | syncTest_Return4~>>() ~>> { i in }
785 | syncTest_ReturnsI+>>(4)
786 | (syncTest_ReturnsI+>>(4)) ~>> { (i: Int) in }
787 | syncTest_ReturnsI+>>(4) ~>> { (i: Int) in }
788 | syncTest_ReturnsI~>>(4)
789 | syncTest_ReturnsI~>>(4) +>> { i in }
790 |
791 | let q = DispatchQueue(label: "dkjfd", attributes: [])
792 |
793 | syncTest_ReturnsI +>> (4) ~>> q ~>> { i in }
794 | syncTest_ReturnsI +>> (4) ~>> q ~>> { i in }
795 | syncTest_ReturnsI +>> (4) ~>> { i in }
796 | syncTest_ReturnsVoid ~>> ()
797 | syncTest_ReturnsVoidParam ~>> q ~>> (3)
798 |
799 | var s: Int = 0;
800 | { s = 3 } +>> ();
801 | { s = 3 }+>>.sync()
802 | let _ = s
803 | }
804 |
805 | func makeSureThisCompiles(_ p: Int, completionHandler: ((_ i: Int) -> Int)?) {
806 | completionHandler ?+>> (3) +>> { i in }
807 | completionHandler?+>>.async(3) +>> { i in }
808 | let _: Int? = completionHandler?~>>.sync(3)
809 | }
810 |
811 |
812 | // MARK: - Async Functions to Test With
813 |
814 | func asyncTest_CallsOnMainReturns4(_ handler: @escaping (Int) -> Void) {
815 | dispatch_main_async { handler(4) }
816 | }
817 |
818 | func asyncTest_CallsOnMainReturnsI(_ i: Int, handler: @escaping (Int) -> Void) {
819 | dispatch_main_async { handler(i) }
820 | }
821 |
822 | func asyncTest_CallsOnMainReturnsIforBoth(_ i: Int, handler: @escaping (_ i: Int, _ s: String) -> Void) {
823 | dispatch_main_async { handler(i, "\(i)") }
824 | }
825 |
826 | // MARK: - Sync Functions to Test With
827 |
828 | func syncTest_ReturnsVoid() {
829 |
830 | }
831 |
832 | func syncTest_ReturnsVoidParam(_ i: Int) {
833 |
834 | }
835 |
836 | func syncTest_Return4() -> Int {
837 | return 4
838 | }
839 |
840 | func syncTest_ReturnsI(_ i: Int) -> Int {
841 | return i
842 | }
843 |
844 | func syncTest_ReturnsIforBoth(_ i: Int) -> (i: Int, s: String) {
845 | return (i: i, s: "\(i)")
846 | }
847 |
848 | }
849 |
850 |
851 |
852 |
--------------------------------------------------------------------------------