Skip to content

Wrapping for PromiseKit #915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
zdnk opened this issue Apr 23, 2019 · 4 comments
Closed

Wrapping for PromiseKit #915

zdnk opened this issue Apr 23, 2019 · 4 comments

Comments

@zdnk
Copy link

zdnk commented Apr 23, 2019

Hello, I am trying to create an API that would allow me to simply use SQLite with Promies from PromiseKit.

I encountered and issue with transactions tho, I you image that every call to run(..) is returning a Promise, then also transaction() needs to return a Promise in the end I'd say.

Is there any example or existing code that shows how to approach this with SQLite?

I was unable to find a way to synchronize it easily.

@zdnk
Copy link
Author

zdnk commented Apr 23, 2019

I was able to do it by wrapping it into a class that has its own queue. Basically at the beginning there will be some main context and each transactions spawns its own context with its own queue. It uses the same connection across the contexts. Also I would implement other methods on the context like run, insert, update etc.

import Foundation
import SQLite
import PromiseKit

class DbContext {
    
    let connection: Connection
    let queue: DispatchQueue
    let parent: DbContext?

    init(with conn: Connection, parent: DbContext? = nil) {
        self.connection = conn
        self.parent = parent
        
        self.queue = DispatchQueue(
            label: "com.zdnkt.robert.DbConext",
            qos: .default,
            attributes: [],
            autoreleaseFrequency: .workItem,
            target: nil
        )
    }
    
    convenience init(parent: DbContext) {
        self.init(with: parent.connection, parent: parent)
    }
    
    @discardableResult
    func transaction(_ closure: @escaping (DbContext) throws -> Promise<Void>) -> Promise<Void> {
        return Promise<Void> { resolver in
            queue.async {
                let childContext = DbContext(parent: self)
                
                do {
                    try childContext.connection.run("BEGIN DEFERRED TRANSACTION")
                } catch {
                    resolver.reject(error)
                    return // Stop here since we cannot establish transaction
                }
                
                do {
                    try closure(childContext).wait()
                    try childContext.connection.run("COMMIT TRANSACTION")
                    resolver.fulfill(())
                } catch {
                    #warning("TODO: fix optional try")
                    try? childContext.connection.run("ROLLBACK TRANSACTION")
                    resolver.reject(error)
                }
            }
        }
    }
}

@ypopovych
Copy link
Collaborator

ypopovych commented Apr 25, 2019

You can use .map and .then in the Promise.
Something like this

return Promise()
    .then(on: queue) { _ in
        try self.connection.run("BEGIN DEFERRED TRANSACTION")
        return try closure(self.connection)
            .map(on: self.queue) { _ in self.connection.run("COMMIT TRANSACTION") }
            .recover(on: self.queue) { err in 
                try self.connection.run("ROLLBACK TRANSACTION")
                throw err
            }
    }

@nathanfallet
Copy link
Collaborator

Linked to #937

@jberkel
Copy link
Collaborator

jberkel commented Aug 24, 2021

The only reactive integration which makes sense at this point is the standard Combine framework.

@jberkel jberkel closed this as completed Aug 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants