Tutorials
No Results

Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Tutorial

Intermediate · 1 hour

Great Causes with Algorand and Swift SDK

This tutorial teaches how Algorand technology could provide a better opportunity to support and inspire developers and organizations on future business transactions.
Great Causes an example application that can inspire and empower people, to support most of the world needs. This tutorial shows you how to build a full iOS App and how you can send a group of transactions to the network using the power of the Swift-Algorand-SDK.

This tutorial provides a practical solution to work as an inspired Kit for future projects.
At the same time, this tutorial shows how the flexibility of Algorand be involved in helping, building and taking care.
Algorand can enable high performing providing security, scalability, speed and low-cost value transfers.
The objective of this tutorial is how to teach you, send more than one transaction to the network using Atomic Transfers with LogicSignature. The Application will be developed using SwiftUI, Swift-Algorand-Sdk and the PureStake API.

Requirements

Xcode 12 Developer Tools Download Here
iOS 14.3 or later
Swift Algorand-Sdk Download Here
PureStake Account Create
TestNet Account See Tutorial
My Algo Algorand Wallet Create

Background

In this Great Causes application, the user can navigate scrolling the Categories Menu and choose a project. The user selects its causes and adds a small Algo donation.
The user can choose more than one cause, meaning that transactions that are part of the transfer either all succeed or all fail.
All the select causes by the user will be traded in Algos, using Atomic Transfers with a Stateless smart contract and signed with LogicSignature.

The PureStake API Service https://developer.purestake.io/
About the PureStake API: https://www.purestake.com/technology/algorand-api/
Code Samples: https://developer.purestake.io/code-samples
API Examples: https://github.com/PureStake/api-examples

Steps

1. Create SwiftUI Project

First let’s start to create your project from the scratch.
EditorImages/2021/08/05 14:02/1_1Step.png
Figure 1-1 create your project

Let’s start with iOS App.
EditorImages/2021/08/05 14:03/1_2step.png
Figure 1-2 Name your project

2. Add Swift Algorand Sdk Packages

First “l like to thank Jesulonimi21 for the Swift Algorand-Sdk.” Go to the following link and copy the main Branch.

EditorImages/2021/08/05 14:05/2_1Git.png
Figure 2-1 Algorand-Sdk
EditorImages/2021/08/05 14:07/2_2packageWhite.png
Figure 2-2 Add Package
EditorImages/2021/08/05 14:07/2_3packageWhite.png
Figure 2-3 Select Branch

3. PureStake API Configuration

PureStake can easily get up and running on the Algorand Network. To know more check this link a walkthrough for using the PureStake API to interact with the Algorand network.

First, need to set the PureStake API address and past your API key on the Token. Please don’t share your API Key.

import SwiftUI
import swift_algorand_sdk

struct donationUI.swift: View {
    var ALGOD_API_ADDR="https://testnet-algorand.api.purestake.io/ps2"
    var ALGOD_API_TOKEN=“Gs——————————————————“
    var ALGOD_API_PORT=""

Second, need to set the client,

let algodClient=AlgodClient(host: ALGOD_API_ADDR, port: ALGOD_API_PORT, token: ALGOD_API_TOKEN)
  algodClient.set(key: “X-API-KeY")

Note: Using these code snippets in the app will be shown in Step 7, in the donationUI.swift.

4. Account Configuration

Now you need to create your Wallet and set your mnemonic. Never share your mnemonic secret key. For this tutorial, I use My Algo Wallet as my main wallet for the transaction. You can go to myalgo and create your account.
For this project you will be needed to set four accounts. One for the sender and the other three as receiver.

First, your mnemonic account as sender,

 let mnemonic = "star star star star star star star star star star star star star star star star star star star star star star star star star”

Three others mnemonic1 to 3 as receiver account,

let mnemonic1 = "fork fork fork fork fork fork fork fork fork fork fork fork fork v fork fork fork fork fork fork fork fork fork fork fork fork”
 let mnemonic2 = "fork fork fork fork fork fork fork fork fork fork fork fork fork v fork fork fork fork fork fork fork fork fork fork fork fork"
 let mnemonic3 = "fork fork fork fork fork fork fork fork fork fork fork fork fork v fork fork fork fork fork fork fork fork fork fork fork fork"

Note: These mnemonics will be added in “data.swift”. These code snippets will be shown in Step 9, in “donationUI.swift”.

5. SwiftUI Views

Using the Xcode project created in Step 1 for the full app “Algo Great Causes” will add the first view “categoriesUI.swift” to the Xcode project.
Go to File/New and then press File…, and create a new SwiftUI View.
EditorImages/2021/08/05 14:21/5_1step.png
Figure 5-1 New SwiftUI View

Create your view and give a name “categoriesUI.swift”.
EditorImages/2021/08/05 14:21/5_2step.png
Figure 5-2 categoriesUI.swift
Repeat the 5-1 Step and add another view “donationUI.swift” to the Xcode project.
EditorImages/2021/08/05 14:22/5_3step.png
Figure 5-3 donationUI.swift

6. Model Data

Data Model is where we add all the data and show it on the App. A struct with properties and an Array with all the data needed for the transaction and graphic interface.
Go to File/New and then press File…, create a new Swift File “data.swift”.
EditorImages/2021/08/05 14:26/6_1step.png
Figure 6-1 Swift File
On the Data Model “data.swift” add your properties and your mnemonic keys.
EditorImages/2021/08/05 14:27/6_2step.png
Figure 6-2 Data Model

Note: Using Identifiable requires an id property. This means all the values are unique.

 let id = UUID()

All Data Model code in step are in the data.swift file.

7. NavigationView

The ContentView
First, we create a NavigationView and add all the content inside. Then add a NavigationLink, allowing users to navigate from a view to another view.
EditorImages/2021/08/05 14:30/7_1step.png
Figure 7-1 ContentView.swift
All code in step are in the ContentView.swift file.

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack() {
                Image("welcomeImg")

                VStack(alignment: .center) {

                    NavigationLink(destination: categoriesUI()) {

                        Image(systemName:"chevron.right.square.fill")

                            .font(.system(size: 70.0))
                            .foregroundColor((Color("seta")))
                    }
                }
                Spacer()
            }
            .edgesIgnoringSafeArea(.top)
            .background((Color("T2")))

        }
        .navigationViewStyle(StackNavigationViewStyle())
    }

}

categoriesUI View
On the “categoriesUI.swift” View created on Step 5 we will declare a list of categories added on the Data Model.
EditorImages/2021/08/05 14:31/7_2step.png
Figure 7-2 List on categoriesUI.swift
All code in step are in the categoriesUI.swift file.

struct categoriesUI: View {
    @State var algoCategories = algoData

    var body: some View {

        List(algoCategories) { EnvioData in
            NavigationLink(
                destination: donationUI(EnvioData: EnvioData)) {
                ZStack () {
                    Image(EnvioData.imageName)
                    Text(EnvioData.title)
                        .foregroundColor(Color("ColorWhite"))
                        .font(.custom("AvenirNext-Bold", size: 24))
                        .fontWeight(.bold)
                        .multilineTextAlignment(.leading)

                        .padding(.top, 35.0)
                        .alignmentGuide(HorizontalAlignment.center) { _ in  150 }
                }
            }
        }
        .listStyle(PlainListStyle())
        .navigationBarTitle("Categories")
    }
}

donationUI View
On the “donationUI.swift” View created on Step 5 we will declare all the code to perform our network transaction.
First, need to “import swift_algorand_sdk“ and set the PureStake API address.
EditorImages/2021/08/05 14:33/7_3step.png
Figure 7-3 PureStake API address
Checkbox functions
When the select a cause for a donation, the Checkbox toggles Bool value to “true”. Then only chooses causes will be added and sent to the network.
EditorImages/2021/08/05 14:37/7_4checkbox.png
Figure 7-4 Checkbox function
Create and declare you own UI.
EditorImages/2021/08/05 14:38/7_5step.png

Figure 7-5 Create Stacks

Note: Don’t forget to add the following line of code on the bottom of the “donationUI.swift” file. Here we can define the index to load when a user selects a category on “categoriesUI.swift”.

struct donationUI_Previews: PreviewProvider {
    static var previews: some View {
        donationUI(EnvioData: algoData[0])
    }
}

All code in step are in the donationUI.swift file.

import SwiftUI
import swift_algorand_sdk

struct donationUI: View {
    @Environment(\.openURL) var openURL
    // MARK: -  Balance
    @State var checkTransaction:String = ""
    @State var balanceAfter:String = ""
    @State var balanceBefore:String = ""

    @State private var assetRewardState = false
    @State var isLoading = false
    let transactionSuccedImage = "orderPlaced"
    let transactionFailImage = "orderfail"
    @State private var compileVerified = false
    @State private var transactionIsShowing = false
    @State private var compileIsPass = false
    @State var complieHash:String = "Hash"
    @State var clearStateProgramSource = "compile.teal"

    // MARK: - PureStake API
    var ALGOD_API_ADDR="https://testnet-algorand.api.purestake.io/ps2"
    var ALGOD_API_TOKEN="G—————————————————l5"
    var ALGOD_API_PORT=""

    // MARK: - data
    let EnvioData:data

    // MARK: - Checkbox values
    @State var titleCodeN1:String = "1 Algo"
    @State var titleCodeN2:String = "1 Algo"
    @State var isSelectboxAllOK:Bool = false
    @State var isSelectboxCodeOK:Bool = false

    @State var isCheckedCodeN1:Bool = false
    @State var isCheckedCodeN2:Bool = false
    @State var isCheckedCodeN3:Bool = false

    // MARK: - Checkbox functions
    func toggleCodeN1(){
       isCheckedCodeN1 = !isCheckedCodeN1
        isSelectboxCodeOK = true
       }

   func toggleCodeN2(){
       isCheckedCodeN2 = !isCheckedCodeN2
        isSelectboxCodeOK = true
          }

    func toggleCodeN3(){
        isCheckedCodeN3 = !isCheckedCodeN3
         isSelectboxCodeOK = true
           }

    // MARK: - Checkbox Aspect
    struct LabelCheckboxText: ViewModifier {

        func body(content: Content) -> some View {
            return  content
                .foregroundColor(Color("T2"))
                .font(.custom("AvenirNext-Bold", size: 20))
                .multilineTextAlignment(.leading)
                .padding(.leading, 15.0)
        }
    }

    struct LabelCheckbox: ViewModifier {

        func body(content: Content) -> some View {
            return  content
                .foregroundColor(Color("T2"))
                .font(.custom("AvenirNext-Bold", size: 24))
                .multilineTextAlignment(.leading)
                .padding(.leading, 15.0)
        }
    }

    struct LabelRecta: ViewModifier {

        func body(content: Content) -> some View {
            return  content
                .foregroundColor((Color("rectTab")))
                .cornerRadius(15)
        }
    }



    var body: some View {
        ScrollView (.vertical, showsIndicators: false) {
            // MARK: - top Image and text
            VStack(alignment: .center) {
                Image("causesTopImage")

                VStack(alignment: .leading) {
                    Text("Atomic Transfer")
                        .font(.title2)
                        .fontWeight(.bold)
                        .multilineTextAlignment(.leading)

                    Text("Select our cause and help.")
                        .multilineTextAlignment(.leading)
                }

                HStack(alignment: .top) {
                    // MARK: - First cause
                    VStack(alignment: .center){

                        ZStack () {
                            Image(EnvioData.imageName1)
                            Image("selectBack")
                                .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                .alignmentGuide(VerticalAlignment.center) { _ in  -130 }

                            VStack(alignment: .leading , spacing: 4) {

                                Button(action: toggleCodeN1){
                                    HStack{
                                        Image(systemName: isCheckedCodeN1 ? "checkmark.square": "square").modifier(LabelCheckbox())

                                        Text(titleCodeN1).modifier(LabelCheckboxText())

                                    }
                                    .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                    .alignmentGuide(VerticalAlignment.center) { _ in  -135 }
                                }
                            }
                        }
                    }
                    .padding(.trailing)

                    // MARK: - Second cause
                    VStack {
                        ZStack () {
                            Image(EnvioData.imageName2)
                            Image("selectBack")
                                .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                .alignmentGuide(VerticalAlignment.center) { _ in  -45 }

                            VStack(alignment: .leading , spacing: 4) {
                                Button(action: toggleCodeN2){

                                    HStack{
                                        Image(systemName: isCheckedCodeN2 ? "checkmark.square": "square").modifier(LabelCheckbox())

                                        Text(titleCodeN2).modifier(LabelCheckboxText())

                                    }
                                    .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                    .alignmentGuide(VerticalAlignment.center) { _ in  -50 }
                                }
                            }
                        }
                    // MARK: - Third cause
                        ZStack () {
                            Image(EnvioData.imageName3)
                            Image("selectBack")
                                .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                .alignmentGuide(VerticalAlignment.center) { _ in  -45 }

                            VStack(alignment: .leading , spacing: 4) {
                                Button(action: toggleCodeN3){

                                    HStack{
                                        Image(systemName: isCheckedCodeN3 ? "checkmark.square": "square").modifier(LabelCheckbox())

                                        Text(titleCodeN2).modifier(LabelCheckboxText())

                                    }
                                    .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                    .alignmentGuide(VerticalAlignment.center) { _ in  -50 }
                                }
                            }
                        }
                    }
            }
            .padding(.bottom)
                // MARK: - Buttons
            HStack(alignment: .top) {

                Button(action: {
                    self.tealCompile()
                    self.getAccountBalance(){amount in
                        print(amount)
                        balanceBefore = String(amount)
                    }
                }) {
                    Image("compileBtn")
                        .padding(.bottom)
                }


                Button(action: {
                    self.createtransactions()
                    self.startHolding()
                }) {
                    Image("donateBtn")
                        .padding(.bottom)
                }
                // MARK: - presentation result
                .sheet(isPresented:  $assetRewardState) {
                    VStack(){
                        if self.transactionIsShowing == true {
                            Image(transactionSuccedImage)
                            Text("Account Balance Before Transaction: \(balanceBefore)")
                            Text("Account Balance After Transaction: \(balanceAfter)")
                            Button("Dismiss",
                                   action: {  self.assetRewardState.toggle() })
                            Button("Check Transaction") {
                                openURL(URL(string: "https://testnet.algoexplorer.io/address/\(String(checkTransaction))")!)
                            }

                        } else  if self.transactionIsShowing == false {
                            Image(transactionFailImage)

                            Button("Dismiss",
                                   action: {  self.assetRewardState.toggle() })
                        }
                    }
                }
                .disabled(compileVerified == false)
                .opacity(compileVerified == false ? 0.0 : 1)
            }
                // MARK: - Compile
                Text(complieHash)
                    .foregroundColor(.black)
                    .padding(.bottom, 50.0)
                // MARK: - Loading
                if isLoading {
                    ZStack {
                        Color(.white)
                            .ignoresSafeArea()
                            .opacity(0.9)

                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle(tint: .black))
                            .scaleEffect(3)
                    }
                    .frame(height: 90.0)
                }
            }

            .background((Color("T2")))
        }
        .edgesIgnoringSafeArea(.top)
    }

8. Teal Compile

First, need to add “compile.teal.txt“ to you Xcode project.

EditorImages/2021/08/17 10:45/8_01step.png
Figure 8-1 Compile.teal.txt

Compile Teal

On a contract account, the TEAL code must be compiled, to return an Algorand address. Before the user can perform the donation, need first to press the compile button. Then the TEAL is signed with your private key and you can give this logic signature to submit the Atomic transactions.
TEAL provides OpCodes allowing the program to get (read) and put (write) data within state storage locations.
Note: Found your account Using Dispenser - TestNet Dispenser link

DEFINE TEAL VERSION
The approval program instructs the Algorand Virtual Machine (AVM) to use “version 4” TEAL OpCodes during execution.
This is required for stateful smart contract applications

#pragma version 4
// This program clears program state
int 1 places the integer 1 on the stack, signaling approval

The following example Teal code please check these two Turtorials by Jason Weathersby, there are very well explained.

Introducing TEAL Version 3

Writing a Simple Smart Contract

// Opt-In Asset Amount is 0
txn AssetAmount
int 0
==
// Check the amount of the spending transaction and verify it is above the minimum giving amount
gtxn 1 Amount
int 10000
>=
return
txn Fee
int 10000
<=

EditorImages/2021/08/05 14:55/8_1step.png
Figure 8-2 Compile Teal

DryRunSource Teal
This is where Smart Contract can be examined. In step 9 this function will be called the source for dryRunSource parameter. 
The call will be made on the line 361 on donationUI.swift.

let source:[Int8] = donationUI.loadSampleTeal2()

EditorImages/2021/08/17 10:48/8_3.png
Figure 8-3 DryRunSource Teal

9. Atomic Transfer

First, need to declare mnemonic values as defined on the Data Model “data.swift”.
EditorImages/2021/08/17 10:49/9_1step.png
Figure 9-1 Mnemonic

Next, we declare an Array that will insert the receiver’s accounts selected by the user if Checkbox Bool value turned to true.
EditorImages/2021/08/17 10:50/9_2.png
Figure 9-2 Array and Checkbox

Dryrun Debugging

A dryrun debugging file will be integrated into our code. The Swift-SDKs support debugging with the dryrun, so when the user presses the donate button, Smart contracts can be examined, debug, approved or rejected.
Algorand provides the Tealdbg command-line tool to launch an interactive session to debug smart contracts. For more information check the following link.

EditorImages/2021/08/17 10:53/9_4.png

Figure 9-3 Dryrun

Algorand Atomic Transfer allows a group of transactions to be submitted at one time, this means that all transactions succeed or all fail.
We can add to the Data Model more accounts if need, up to 16 total transactions of any type.
Please, check Algorand Atomic Transfers by Russ Fustino to now more about Atomic Transfers.

In Figure 9-4 we add all transactions into one, Signed the grouped transactions with all the appropriate keys and submit them to the network

EditorImages/2021/08/17 10:55/9_3step.png
Figure 9-4 Atomic Transfer

Next, If dryRunRequest gets a successful response, the transaction will be send to the network.
EditorImages/2021/08/17 10:57/9_5step.png
Figure 9-5 dryRunRequest

All code in step are in the donationUI.swift file.

import SwiftUI
import swift_algorand_sdk

struct donationUI: View {
    @Environment(\.openURL) var openURL
    // MARK: -  Balance
    @State var checkTransaction:String = ""
    @State var balanceAfter:String = ""
    @State var balanceBefore:String = ""

    @State private var assetRewardState = false
    @State var isLoading = false
    let transactionSuccedImage = "orderPlaced"
    let transactionFailImage = "orderfail"
    @State private var compileVerified = false
    @State private var transactionIsShowing = false
    @State private var compileIsPass = false
    @State var complieHash:String = "Hash"
    @State var clearStateProgramSource = "compile.teal"

    // MARK: - PureStake API
    var ALGOD_API_ADDR="https://testnet-algorand.api.purestake.io/ps2"
    var ALGOD_API_TOKEN="G—————————————————l5"
    var ALGOD_API_PORT=""

    // MARK: - data
    let EnvioData:data

    // MARK: - Checkbox values
    @State var titleCodeN1:String = "1 Algo"
    @State var titleCodeN2:String = "1 Algo"
    @State var isSelectboxAllOK:Bool = false
    @State var isSelectboxCodeOK:Bool = false

    @State var isCheckedCodeN1:Bool = false
    @State var isCheckedCodeN2:Bool = false
    @State var isCheckedCodeN3:Bool = false

    // MARK: - Checkbox functions
    func toggleCodeN1(){
       isCheckedCodeN1 = !isCheckedCodeN1
        isSelectboxCodeOK = true
       }

   func toggleCodeN2(){
       isCheckedCodeN2 = !isCheckedCodeN2
        isSelectboxCodeOK = true
          }

    func toggleCodeN3(){
        isCheckedCodeN3 = !isCheckedCodeN3
         isSelectboxCodeOK = true
           }

    // MARK: - Checkbox Aspect
    struct LabelCheckboxText: ViewModifier {

        func body(content: Content) -> some View {
            return  content
                .foregroundColor(Color("T2"))
                .font(.custom("AvenirNext-Bold", size: 20))
                .multilineTextAlignment(.leading)
                .padding(.leading, 15.0)
        }
    }

    struct LabelCheckbox: ViewModifier {

        func body(content: Content) -> some View {
            return  content
                .foregroundColor(Color("T2"))
                .font(.custom("AvenirNext-Bold", size: 24))
                .multilineTextAlignment(.leading)
                .padding(.leading, 15.0)
        }
    }

    struct LabelRecta: ViewModifier {

        func body(content: Content) -> some View {
            return  content
                .foregroundColor((Color("rectTab")))
                .cornerRadius(15)
        }
    }



    var body: some View {
        ScrollView (.vertical, showsIndicators: false) {
            // MARK: - top Image and text
            VStack(alignment: .center) {
                Image("causesTopImage")

                VStack(alignment: .leading) {
                    Text("Atomic Transfer")
                        .font(.title2)
                        .fontWeight(.bold)
                        .multilineTextAlignment(.leading)

                    Text("Select our cause and help.")
                        .multilineTextAlignment(.leading)
                }

                HStack(alignment: .top) {
                    // MARK: - First cause
                    VStack(alignment: .center){

                        ZStack () {
                            Image(EnvioData.imageName1)
                            Image("selectBack")
                                .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                .alignmentGuide(VerticalAlignment.center) { _ in  -130 }

                            VStack(alignment: .leading , spacing: 4) {

                                Button(action: toggleCodeN1){
                                    HStack{
                                        Image(systemName: isCheckedCodeN1 ? "checkmark.square": "square").modifier(LabelCheckbox())

                                        Text(titleCodeN1).modifier(LabelCheckboxText())

                                    }
                                    .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                    .alignmentGuide(VerticalAlignment.center) { _ in  -135 }
                                }
                            }
                        }
                    }
                    .padding(.trailing)

                    // MARK: - Second cause
                    VStack {
                        ZStack () {
                            Image(EnvioData.imageName2)
                            Image("selectBack")
                                .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                .alignmentGuide(VerticalAlignment.center) { _ in  -45 }

                            VStack(alignment: .leading , spacing: 4) {
                                Button(action: toggleCodeN2){

                                    HStack{
                                        Image(systemName: isCheckedCodeN2 ? "checkmark.square": "square").modifier(LabelCheckbox())

                                        Text(titleCodeN2).modifier(LabelCheckboxText())

                                    }
                                    .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                    .alignmentGuide(VerticalAlignment.center) { _ in  -50 }
                                }
                            }
                        }
                    // MARK: - Third cause
                        ZStack () {
                            Image(EnvioData.imageName3)
                            Image("selectBack")
                                .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                .alignmentGuide(VerticalAlignment.center) { _ in  -45 }

                            VStack(alignment: .leading , spacing: 4) {
                                Button(action: toggleCodeN3){

                                    HStack{
                                        Image(systemName: isCheckedCodeN3 ? "checkmark.square": "square").modifier(LabelCheckbox())

                                        Text(titleCodeN2).modifier(LabelCheckboxText())

                                    }
                                    .alignmentGuide(HorizontalAlignment.center) { _ in  70 }
                                    .alignmentGuide(VerticalAlignment.center) { _ in  -50 }
                                }
                            }
                        }
                    }
            }
            .padding(.bottom)
                // MARK: - Buttons
            HStack(alignment: .top) {

                Button(action: {
                    self.tealCompile()
                    self.getAccountBalance(){amount in
                        print(amount)
                        balanceBefore = String(amount)
                    }
                }) {
                    Image("compileBtn")
                        .padding(.bottom)
                }


                Button(action: {
                    self.createtransactions()
                    self.startHolding()
                }) {
                    Image("donateBtn")
                        .padding(.bottom)
                }
                // MARK: - presentation result
                .sheet(isPresented:  $assetRewardState) {
                    VStack(){
                        if self.transactionIsShowing == true {
                            Image(transactionSuccedImage)
                            Text("Account Balance Before Transaction: \(balanceBefore)")
                            Text("Account Balance After Transaction: \(balanceAfter)")
                            Button("Dismiss",
                                   action: {  self.assetRewardState.toggle() })
                            Button("Check Transaction") {
                                openURL(URL(string: "https://testnet.algoexplorer.io/address/\(String(checkTransaction))")!)
                            }

                        } else  if self.transactionIsShowing == false {
                            Image(transactionFailImage)

                            Button("Dismiss",
                                   action: {  self.assetRewardState.toggle() })
                        }
                    }
                }
                .disabled(compileVerified == false)
                .opacity(compileVerified == false ? 0.0 : 1)
            }
                // MARK: - Compile
                Text(complieHash)
                    .foregroundColor(.black)
                    .padding(.bottom, 50.0)
                // MARK: - Loading
                if isLoading {
                    ZStack {
                        Color(.white)
                            .ignoresSafeArea()
                            .opacity(0.9)

                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle(tint: .black))
                            .scaleEffect(3)
                    }
                    .frame(height: 90.0)
                }
            }

            .background((Color("T2")))
        }
        .edgesIgnoringSafeArea(.top)
    }

    // MARK: - function Balance

    func getAccountBalance(functionToCall: @escaping (String)->Void){
        let algodClient=AlgodClient(host: ALGOD_API_ADDR, port: ALGOD_API_PORT, token: ALGOD_API_TOKEN)
        algodClient.set(key: "X-API-KeY")
        let mnemonic = EnvioData.mnemonic
        let account = try! Account(mnemonic)
        algodClient.accountInformation(address: account.address.description).execute(){accountInformationResponse in
            if(accountInformationResponse.isSuccessful){
                print("\(accountInformationResponse.data!.amount!)")
                checkTransaction = (String(account.address.description))
                functionToCall("\(accountInformationResponse.data!.amount!)")
            }else{
                functionToCall("\(String(describing: accountInformationResponse.errorDescription))")

            }
        }

    }

    // MARK: - function Loading
    func startHolding(){
        isLoading = true

    }

    func startHoldingOff(){
        isLoading = false

    }
    // MARK: - function tealCompile
    public func tealCompile(){

        let algodClient=AlgodClient(host: ALGOD_API_ADDR, port: ALGOD_API_PORT, token: ALGOD_API_TOKEN)
        algodClient.set(key: "X-API-KeY")

        let source:[Int8] = donationUI.loadSampleTeal(resource: clearStateProgramSource)

        algodClient.tealCompile().source(source: source).execute(){compileResponse in
            if(compileResponse.isSuccessful){
                print(compileResponse.data?.hash as Any)
                print(compileResponse.data?.result as Any)
                self.compileVerified = true
                self.complieHash = "Result: \(compileResponse.data!.result!)\n Hash: \(compileResponse.data!.hash!)"
            }else{
                print(compileResponse.errorMessage!)
                complieHash = compileResponse.errorMessage!
            }
        }
        print(source)
    }

    public static func loadSampleTeal(resource:String)  -> [Int8] {
        let configURL = Bundle.main.path(forResource: resource, ofType: "txt")
        let contensts = try! String(contentsOfFile: configURL!.description)
        let jsonData = contensts.data(using: .utf8)!
        let  data = CustomEncoder.convertToInt8Array(input: Array(jsonData))
        print(data)
        return data
    }

    // MARK: - function tealCompile Dryrun

    public static func loadSampleTeal2()  -> [Int8] {
        let configURL = Bundle.main.path(forResource: "compile.teal", ofType: "txt")
        let contensts = try! String(contentsOfFile: configURL!.description)
        let jsonData = contensts.data(using: .utf8)!

        let  data = CustomEncoder.convertToInt8Array(input: Array(jsonData))
        print(data)
        return data
       }


    // MARK: - function transaction
    func createtransactions(){

        let algodClient=AlgodClient(host: ALGOD_API_ADDR, port: ALGOD_API_PORT, token: ALGOD_API_TOKEN)
        algodClient.set(key: "X-API-KeY")


        // MARK: - data.swift
        let mnemonic = EnvioData.mnemonic
        let mnemonic1 = EnvioData.mnemonic1
        let mnemonic3 = EnvioData.mnemonic3
        let mnemonic2 = EnvioData.mnemonic2

        let account = try! Account(mnemonic)
        let account1 = try! Account(mnemonic1)
        let account2 = try! Account(mnemonic2)
        let account3 = try! Account(mnemonic3)
        let senderAddress = account.getAddress()
        let receiverAddress1 = account1.getAddress()
        let receiverAddress2 = account2.getAddress()
        let receiverAddress3 = account3.getAddress()

        algodClient.transactionParams().execute(){ paramResponse in
            if(!(paramResponse.isSuccessful)){
                print(paramResponse.errorDescription!);
                print("OK")
                return;
            }

            // MARK: - DryrunRequest
            var sources:[DryrunSource] = Array()
            var stxns:[SignedTransaction] = Array()
            let source:[Int8] = donationUI.loadSampleTeal2()
            let dryRunSource = DryrunSource()
            let dryRunRequest = DryrunRequest()
            dryRunSource.fieldName =  "approv"
            dryRunSource.source =  String(data: Data(CustomEncoder.convertToUInt8Array(input:  source)),encoding: .utf8)!
            dryRunSource.txnIndex = 01
            sources.append(dryRunSource)

            let program:[Int8] = CustomEncoder.convertToInt8Array(input: CustomEncoder.convertBase64ToByteArray(data1: "ASABASI="))

            let lsig = try! LogicsigSignature(logicsig: program)

            _ = try! account.signLogicsig(lsig: lsig)
            // MARK: - transaction array
            var transactions1:[swift_algorand_sdk.Transaction] = Array()

            // MARK: - Check causes selected
            if isCheckedCodeN1 == true {
            let tx1 = try! Transaction.paymentTransactionBuilder().setSender(senderAddress)
             .amount(1000000)
             .receiver(receiverAddress1)
             .note("Swift Algo sdk is cool".bytes)
             .suggestedParams(params: paramResponse.data!)
             .build()

            transactions1.append(tx1)
        }

            if isCheckedCodeN2 == true {
            let tx2 = try! Transaction.paymentTransactionBuilder().setSender(senderAddress)
              .amount(1000000)
              .receiver(receiverAddress2)
              .note("Swift Algo sdk is cool".bytes)
              .suggestedParams(params: paramResponse.data!)
              .build()
                transactions1.append(tx2)
            }
            if isCheckedCodeN3 == true {
            let tx3 = try! Transaction.paymentTransactionBuilder().setSender(senderAddress)
              .amount(1000000)
              .receiver(receiverAddress3)
              .note("Swift Algo sdk is cool".bytes)
              .suggestedParams(params: paramResponse.data!)
              .build()
                transactions1.append(tx3)
            }
            // MARK: - Signed Transaction and Atomic Transfer
            let gid = try! TxGroup.computeGroupID(txns: transactions1)
            var signedTransactions:[SignedTransaction?]=Array(repeating: nil, count: transactions1.count)
            for i in 0..<transactions1.count{
                transactions1[i].assignGroupID(gid: gid)
                signedTransactions[i]=Account.signLogicsigTransaction(lsig: lsig,tx: transactions1[i])
                stxns.append(signedTransactions[i]!)
                dryRunRequest.sources = sources
                dryRunRequest.txns = stxns
            }

            _ = CustomEncoder.encodeToJson(obj: dryRunRequest)
            var encodedTrans:[Int8]=Array()
            for i in 0..<signedTransactions.count{
                encodedTrans = encodedTrans+CustomEncoder.encodeToMsgPack(signedTransactions[i])
            }
            algodClient.tealDryRun().request(request: dryRunRequest).execute(){ requestResponse in
//
                        if(requestResponse.isSuccessful){
                            print(requestResponse.data!.toJson() as Any)

                            algodClient.rawTransaction().rawtxn(rawtaxn: encodedTrans).execute(){
                                                  response in
                                if(response.isSuccessful){

                                    print(response.data!.txId)
                                    print("Pass")
                                    self.waitForTransaction(txId:response.data!.txId)


                                }else{
                                    print(response.errorDescription!)
                                    print("Failed")
                                }
                            }



                            print("Created")
                        }else{
                            print("Failed")
                        }
                    }
        }
    }

    func waitForTransaction(txId:String) {

        let algodClient=AlgodClient(host: ALGOD_API_ADDR, port: ALGOD_API_PORT, token: ALGOD_API_TOKEN)
        algodClient.set(key: "X-API-KeY")
        var confirmedRound: Int64?=0
        algodClient.pendingTransactionInformation(txId:txId).execute(){
            pendingTransactionResponse in
            if(pendingTransactionResponse.isSuccessful){
                confirmedRound=pendingTransactionResponse.data!.confirmedRound
                // MARK: - Show the result
                if(confirmedRound != nil && confirmedRound! > 0){
                    print("confirmedRound")
                    self.transactionIsShowing = true
                    self.startHoldingOff()
                    if transactionIsShowing == true {
                        DispatchQueue.main.async {
                            print(transactionIsShowing)
                            self.assetRewardState = true
                            self.getAccountBalance(){amount in
                                balanceAfter = String(amount)
                            }
                        }
                    }
                }else{
                    self.waitForTransaction(txId: txId)
                }
            }else{
                print(pendingTransactionResponse.errorDescription!)
                self.transactionIsShowing = false
                self.startHoldingOff()
                if transactionIsShowing == false {
                    DispatchQueue.main.async {
                        print("fail")
                        self.assetRewardState = true

                    }
                }
                confirmedRound=12000;
            }
        }
    }
}

struct donationUI_Previews: PreviewProvider {
    static var previews: some View {
        donationUI(EnvioData: algoData[0])
    }
}

10. Execute and Check

After select the donate causes, need to press the compile button.
EditorImages/2021/08/17 10:59/10_1.png
Figure 10-1 Compile Button
After TEAL code is compiled is approved, the user can now submit the transaction to the network pressing the donate button.The account balance before and after is presented here.

On the Figure 10-2 can check the transaction result.
EditorImages/2021/08/17 10:59/10_2.png
Figure 10-2 Transaction
EditorImages/2021/08/05 15:19/transaction10_3.png
Figure 10-3 Transaction Confirm

Press Check Transaction button and confirm the transaction in a WebView.
EditorImages/2021/08/17 15:35/10_3.png
Figure 10-4 Check Transaction
EditorImages/2021/08/17 15:23/10_4.png
Figure 10-5 WebView

11. Github Project

Go to the following link and try the full project.
https://github.com/3dlifee/AlgoGreatCauses.git
Thanks.

Warning

This tutorial is intended for learning purposes only. It does not cover error checking and other edge cases. The smart contract(s) in this solution have NOT been audited. Therefore, it should not be used as a production application

August 26, 2021