Last active
August 18, 2018 23:52
-
-
Save shaps80/792038ec43d1c976646c36fe9956514c to your computer and use it in GitHub Desktop.
A nestable transaction handler in Swift.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
public final class Transaction { | |
public enum Error: Swift.Error { | |
case notInTransaction | |
} | |
private static let key = DispatchSpecificKey<Void>() | |
public static func validate() throws { | |
guard DispatchQueue.main.getSpecific(key: Transaction.key) != nil else { | |
throw Error.notInTransaction | |
} | |
} | |
public func performUpdates(_ updates: @escaping () -> Void, completion: (() -> Void)?) { | |
#if DEBUG | |
let block = { | |
let value: Void? = DispatchQueue.main.getSpecific(key: Transaction.key) | |
if value == nil { | |
DispatchQueue.main.setSpecific(key: Transaction.key, value: ()) | |
} | |
updates() | |
if value == nil { | |
DispatchQueue.main.setSpecific(key: Transaction.key, value: nil) | |
} | |
} | |
#else | |
// For non-DEBUG builds we can skip the marker and just execute | |
// This may improve RELEASE builds performance | |
let block = updates | |
#endif | |
block() | |
completion?() | |
} | |
public init() { } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XCTest | |
import Quick | |
import Nimble | |
final class Transaction_Spec: QuickSpec { | |
override func spec() { | |
context("Given a transaction") { | |
it("it should validate when called inside a transaction") { | |
let expect = self.expectation(description: "Inside transaction") | |
Transaction().performUpdates({ | |
do { | |
try Transaction.validate() | |
expect.fulfill() | |
} catch { } | |
}, completion: nil) | |
} | |
it("it should throw when called inside a transaction but dispatched to another queue") { | |
let expect = self.expectation(description: "Inside transaction but dispatched") | |
Transaction().performUpdates({ | |
DispatchQueue.global().async { | |
do { | |
try Transaction.validate() | |
} catch { | |
expect.fulfill() | |
} | |
} | |
}, completion: nil) | |
} | |
it("it should throw when called outside a transaction") { | |
let expect = self.expectation(description: "Inside transaction") | |
do { | |
try Transaction.validate() | |
} catch { | |
expect.fulfill() | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example: