TheChain 1.2.0

TheChain 1.2.0

Maintained by Dmitry.



TheChain 1.2.0

  • By
  • Dmitry

CI Status Version License Platform

TheChain

TheChain helps you write more clearer and readable async code:

override func viewDidLoad() {
    super.viewDidLoad()
    
    downloadImageData().onStart {
        print("Downloading started")
    }.onCompletion {
        print("Downloading finished")
    }.then(
        cacheDownloadedData
    ).then(
        readImageFromCache
    ).catch { (error) in
        print("❌ Failed to load image: \(error.localizedDescription)")
    }.finally(on: .main) { (image) in
        self.imageView.image = image
    }
}
    
// MARK: - Async
    
private func downloadImageData() -> Link<Data> {
    return URLSession.shared.dataTaskLink(with: "https://lorempixel.com/1000/1000/")
}

private func cacheDownloadedData(_ data: Data) -> Link<Void> {
    return Link(on: .global(), {
        let url = try URL.imageDataFileUrl.orThrow(Error.fileUrlIsNil)
            
        try data.write(to: url)
    })
}
    
private func readImageFromCache() -> Link<UIImage> {
    return Link(onGlobalQueue: {
        let url = try URL.imageDataFileUrl.orThrow(Error.fileUrlIsNil)
            
        let data = try Data(contentsOf: url)
            
        return try UIImage(data: data).orThrow(Error.unableToInitImage)
    })
}

Basic usage

All you need is create new Link object with desired result type, run you async code and pass result or error when async work done.

func asyncMethod() -> Link<String> {
    let link = Link<String>()
    
    DispatchQueue.global().async {
        //Some async work goes here
        link.finish("Async work result")
        
        //or
        //link.throw(Error.someError)
    }
    
    return link
}

func someMethod() {
    asyncMethod().catch { (error) in
        // Handle error
    }.finally { (result) in
        // Handle the String result
    }
}

Chaining

You can create a chain using Links just like this:

func asyncFoo() -> Link<String> {
    ...
}

func asyncBar(_ input: String) -> Link<Double> {
    ...
}

func asyncBaz(_ input: Double) -> Link<Bool> {
    ...
}

asyncFoo().then(asyncBar).then(asyncBaz).catch { (error) in
    // Handle error
}.finally { (result) in
    // Handle the Bool result
}

Extensions

TheChain comes with some handy extensions which helps you write even more compact, boilerplate-free code.

DispatchQueue

func readImageFromCache() -> Link<UIImage> {
    return Link(onGlobalQueue: {
        let url = try URL.imageDataFileUrl.orThrow(Error.fileUrlIsNil)
            
        let data = try Data(contentsOf: url)
            
        return try UIImage(data: data).orThrow(Error.unableToInitImage)
    })
}

Instead of:

func readImageFromCache() -> Link<UIImage> {
    let link = Link<UIImage>()
    
    DispatchQueue.global().async {
        guard let url = URL.imageDataFileUrl else {
            return link.throw(Error.fileUrlIsNil)
        }
        
        do {
            let data = try Data(contentsOf: url)
            
            guard let image = UIImage(data: data) else {
                return link.throw(Error.unableToInitImage)
            }
            
            link.finish(image)
        } catch {
            link.throw(error)
        }
    }
    
    return link
}

URLSession

func loadPosts() -> Link<[Post]> {
    return URLSession.shared.dataTaskLink(with: "https://jsonplaceholder.typicode.com/posts")
}

Instead of:

func loadPosts() -> Link<[Post]> {
    let link = Link<[Post]>()
    
    guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
        return link.throw(Error.unableToInitUrl)
    }
    
    URLSession.shared.dataTask(with: URLRequest(url: url)) { (data, _, error) in
        if let data = data {
            do {
                let posts = try JSONDecoder().decode([Post].self, from: data)
                
                link.finish(posts)
            } catch {
                link.throw(error)
            }
        } else if let error = error {
            link.throw(error)
        }
    }.resume()
    
    return link
}

Installation

TheChain is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'TheChain'

License

TheChain is available under the MIT license. See the LICENSE file for more info.