Last active
October 26, 2017 18:23
-
-
Save JohnSundell/0163219c7438f6b6656bb5455ec62d76 to your computer and use it in GitHub Desktop.
This is a great technique if you need to interact with singleton-based APIs but still have great testability
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 UIKit | |
// Create a protocol that defines what APIs that you need from the singleton | |
protocol Application { | |
func open(url: URL) | |
} | |
// Make the singleton-based class conform to your protocol | |
extension UIApplication: Application { | |
func open(url: URL) { | |
open(url, options: [:], completionHandler: nil) | |
} | |
} | |
class Navigator { | |
private let application: Application | |
// Use a default argument to avoid increasing the complexity of the API, while still enabling dependency injection | |
init(application: Application = UIApplication.shared) { | |
self.application = application | |
} | |
func navigate(to url: URL) { | |
application.open(url: url) | |
} | |
} | |
// Our Navigator class can now be used like this in our production code: | |
let navigator = Navigator() | |
// And in our tests, we can inject a mocked Application | |
class ApplicationMock: Application { | |
private(set) var openedURLs = [URL]() | |
func open(url: URL) { | |
openedURLs.append(url) | |
} | |
} | |
let application = ApplicationMock() | |
let navigator = Navigator(application: application) | |
let url = URL(string: "https://github.com")! | |
navigator.navigate(to: url) | |
XCTAssertEqual(application.openedURLs, [url]) |
That seems easy for functions with no return value but what if it is something like
public func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> ()) -> URLSessionDataTask
from URLSession
singleton
@kdawgwilk I think you can return
anything. URLSessionDataTask
is returned immediately, right? So why not pass it back to protocol
method?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great stuff!