Skip to content
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

Feature Request: Creating a wrapper to expose the client methods using async await #27

Open
Vilijan opened this issue Feb 24, 2022 · 3 comments

Comments

@Vilijan
Copy link

Vilijan commented Feb 24, 2022

Using the latest Swift's practices for Concurrency will make the code that is using the Swift-Algorand-SDK a lot more readable. The idea would be to create a wrapper on top of the current methods that use the async await syntax instead of competition handlers.

A sample example implementation of some of the most common methods:

func suggestedParameters() async throws -> TransactionParametersResponse {
        return try await withCheckedThrowingContinuation({ continuation in
            DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
                self.client.transactionParams().execute(){ paramResponse in
                    if(!(paramResponse.isSuccessful)){
                        continuation.resume(throwing: GeneralError.error)
                    } else {
                        continuation.resume(returning: paramResponse.data!)
                    }
                }
            }
        })
    }
 
 func waitForConfirmation(transactionID: String) async throws -> String {
        return try await withCheckedThrowingContinuation({ continuation in
            DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
                self.client.pendingTransactionInformation(txId: transactionID).execute() { response in
                    if(response.isSuccessful){
                        continuation.resume(returning: transactionID)
                    }else{
                        continuation.resume(throwing: GeneralError.error)
                    }
                }
            }
        })
 }
 
 func executeTransaction(encodedTransaction: [Int8]) async throws -> String {
        let transactionID: String = try await withCheckedThrowingContinuation({ continuation in
            DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
                self.client.rawTransaction(rawtxn: encodedTransaction).execute(){ response in
                    if(response.isSuccessful){
                        continuation.resume(returning: response.data!.txId)
                    }else{
                        continuation.resume(throwing: GeneralError.error)
                    }
 
                }
            }
        })
 
        return try await waitForConfirmation(transactionID: transactionID)
   }

Then we can use those methods, which makes the code a lot more readable.
Sample of creating new asset:

let account = try! Account()

let suggestedParams = try await self.suggestedParameters()
 
let create_asa_txn = try! Transaction.assetCreateTransactionBuilder()
              .setSender(sdk_account.address)
              .setAssetTotal(assetTotal: 1)
              .setAssetDecimals(assetDecimals:  0)
              .assetUnitName(assetUnitName: "APPL")
              .assetName(assetName:  "Swift SDK")
              .manager(manager: sdk_account.address)
              .reserve(reserve: sdk_account.address)
              .defaultFrozen(defaultFrozen:  false)
              .suggestedParams(params: suggestedParams)
              .build()
 
 let signedTransaction = account.signTransaction(tx: create_asa_txn)
 
 let transactionID =  try await self.executeTransaction(encodedTransaction: CustomEncoder.encodeToMsgPack(signedTransaction))
@stefanomondino
Copy link
Contributor

Hi @Vilijan this seems like a reasonable request and I can definetly take a look at it, but I'm a little bit concerned about the supported min iOS version (currently targeting iOS 10.0 with cocoapods) while Swift Concurrency is only available starting from iOS 13.0.
I think there's a workaround using #if canImport but i just want to make sure this doesn't introduce any breaking change to other users.

I'll dig into it asap :)

@stefanomondino
Copy link
Contributor

hi @Vilijan, sorry for the delay.
Turns out this could be quite simple i guess, with something like this (work in progress)

public extension Request {
    func execute() async throws -> Response<ResponseType> {
        try await withCheckedThrowingContinuation({ continuation in
            self.execute {
                continuation.resume(returning: $0)
            }
        })
    }
}

In this way every client operation could be easily used like this:

let suggestedParams = try await self.client.transactionParams().execute()

what do you think? Can you give it a shot in your project?
Also I'd agree with you in having functions throwing errors, but doing that would require us to review all current method signatures introducing lots of breaking changes and I honestly don't know if it's worth it

@stefanomondino
Copy link
Contributor

(Btw: I've using a throwing function in my example anyway so that at least the async implementation is future proof)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants