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

Developing iOS App Clips Using Swift SDK

This tutorial teaches you how to use an App Clip a lightweight version of your app to order coffee roasters. Learn how to implement App Clips in your iOS apps and perform transactions with the Algorand network.
The power of App Clips to quickly perform one Algorand transaction of several available potentials in an existing app avoiding the need to download and install from the App Store the full App for the user. Algorand enables high performance providing security, scalability, speed and low-cost value transfers and it makes the perfect combination on the growth of the digital economy.
The objective of this tutorial is how to implement an App Clip Application creating a separate target and sharing the code between your App Clip and full app. Using SwiftUI, Swift-Algorand-Sdk and the PureStake API will be making a transaction in Algorand using LogicSignature.

Requirements

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

Background

In this ZCoffee application, the user will discover the App Clip by using an NFC scan-only App Clip Code. The user will scan the NFC Tag Reader with the Camera to launch the App Clip. After the scan, the App Clip receives an invocation URL with the information that will be showing on the App Clip card.
At this moment an instant purchase can be done using the App Clip and avoiding installing the full App. The system automatically removes the App Clip after a period of inactivity.
The transaction will be made in Algos with a Stateless smart contract and be 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
Creating App Clip Codes link

Steps

1. Create SwiftUI Project

First, let’s start to create your project from scratch.
Note: You can follow the video at the start to help you build your project.

EditorImages/2021/06/20 12:01/1_1create.png
Figure 1-1 create your project

Let’s start with iOS App.
EditorImages/2021/06/20 12:02/1_2create.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/06/20 12:07/3Git.png
Figure 2-1 Algorand-Sdk

EditorImages/2021/06/20 12:08/4packageWhite.png
Figure 2-2 Add Package

EditorImages/2021/06/20 12:07/4-1packageWhite.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 ZCoffeeSwiftUIView.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 8, in the ContentView.

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 https://wallet.myalgo.com/access and create your account.
For this project you will be needed to set two accounts on the ZCoffeeSwiftUIView.swift, one as senderAddress and other as receiverAddress.

    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” 

    let mnemonic2 = "fork fork fork fork fork fork fork fork cancel fork fork fork fork fork fork fork fork fork fork fork fork fork fork fork fork"

    let account2 = try! Account(mnemonic2)
    let account = try! Account(mnemonic)
    let senderAddress = account.getAddress()
    let receiverAddress2 = account2.getAddress()

Note: Using these code snippets in the app will be shown in Step 9, in the ContentView.

5. Create App Clip target

Using the Xcode project created in Step 1 for the full app “ZCoffee Clip” will add an App Clip target to the Xcode project.

EditorImages/2021/06/20 16:21/5_1newfile.png

Figure5-1 Add Target
Choose App Clip and give a name.
EditorImages/2021/06/20 16:22/5_2appclip.png

Figure 5-2 App Clip
EditorImages/2021/06/20 16:24/5_3appclip.png
Figure 5-3 Name App Clip Target

App Clip Target created
EditorImages/2021/06/20 16:24/5_4appclip.png
Figure 5-4 App Clip ContentView.swift

Add swift-algorand-sdk on the ZCoffeeTarget
EditorImages/2021/06/20 16:25/5_5algokit.png
Figure 5-5 Add swift-algorand-sdk

Select and add the swift-algorand-sdk
EditorImages/2021/06/20 16:26/5_6algokit.png
Figure 5-6 Select swift-algorand-sdk

Run the App Clip in Simulator or on a device and you can view an empty white screen because nothing was added to the App Clip target.

6. Creating App Clip Codes

Using an NFC scan-only App Clip Code is a great way to launch an App Clip.
Now, let’s start creating an App Clip Code and Encoding a URL in an App Clip Code :

1: Install App Clip Code Generator (https://developer.apple.com/app-clips/resources/)

EditorImages/2021/06/20 16:29/6_1appclipgenerator.png
Figure 6-1 App Clip Code Generator

2: Go to Finder/Go/Utilities and open Terminal (command-line tool).
Open the Sample Templates folder and choose one of the available colours.

open /Library/Developer/AppClipCodeGenerator/SampleTemplates

EditorImages/2021/06/20 16:34/6_2terminal.png
Figure 6-2 SampleTemplates

3: Configure and link the App Clip.

AppClipCodeGenerator generate --type nfc --url https://example.com --index 0 --output zcoffee.svg

EditorImages/2021/06/20 16:36/6_3terminalurl.png
Figure 6-3 Configure and link the App Clip

4: Write on the Terminal command line: “open zcoffee.svg”

open zcoffee.svg

You can Print Printing Guidelines
or use digitally the App Clip Code.

EditorImages/2021/06/20 16:38/6_4code.png
Figure 6-4 App Clip Code

7. Local Experiences

Local experiences let you test your projects before they are ready to submit to Apple Store. To create a local experience, on your device iPhone or iPad, go to Developer Settings and select Local Experiences.
You need to fulfil the following fields: App Clips name, Title, URL prefix, bundleID and select an image as the header image.
Now scan your NFC tag to launch the experience.

EditorImages/2021/06/20 17:14/7_1Localexperience.jpg
Figure 7-1 App Clip Card

1: On your device go to Developer Settings and select Local Experiences.

EditorImages/2021/06/20 17:15/7_2Localexperience.PNG
Figure 7-2 Local Experiences
2: Create a Local Experience and fulfil the following fields.

EditorImages/2021/06/20 17:16/7_3Localexperience.png
Figure 7-3 Fulfil the fields

Note: Your Bundle Identifier can be fund on ZCoffee Xcode project.

EditorImages/2021/06/20 17:17/7_4Localexperience.png
Figure 7-4 Bundle Identifier

8. ZCoffee View

The ZCoffeeSwiftUIView.swift
When the App starts, it displays this instance “ZCoffeeSwiftUIView.swift“.
Because SwitftUI is declarative, here you can create your UI and perform the actions needed to run the App Clip.
To start go to File/New and then press File….

EditorImages/2021/06/20 17:19/9_1Zcoffee.png
Figure 8-1 Create new file

Choose SwiftUI View as the template.

EditorImages/2021/06/20 17:20/9_2Zcoffee.png
Figure 8-2 Template

Give a name and check all the targets.

EditorImages/2021/06/20 17:23/9_3Zcoffee.png
Figure 8-3 Name the template

First, need to “import swift_algorand_sdk“ and set the PureStake API address.

EditorImages/2021/06/20 17:29/9_4Zcoffee.png
Figure 8-4 PureStake API address

Second, create and declare you own UI.

EditorImages/2021/06/20 17:24/9_5Zcoffee.png

All UI and actions code in step are in the ZCoffeeSwiftUIView.swift file.

  VStack {

            Image("appclippBuy")
                .resizable()

            HStack() {

                Button(action: {

                    self.tealCompile()

                }) {
                    Image("compile")
                        .padding(.bottom)

                }
                .padding()

                Spacer()

                Button(action: {

                    self.createApplication()
                    self.startHolding()
                }) {
                    Image("buyBtn")

                }

                .sheet(isPresented:  $assetRewardState) {
                    VStack(){
                        if self.transactionIsShowing == true {

                            Image(transactionSuccedImage)

                            Button("Dismiss",
                                   action: {  self.assetRewardState.toggle() })

                        } else  if self.transactionIsShowing == false {


                            Image(transactionFailImage)

                            Button("Dismiss",
                                   action: {  self.assetRewardState.toggle() })

                        }
                    }
                }

                .disabled(compileVerified == false)
                .opacity(compileVerified == false ? 0.6 : 1)

            }
            .padding(.horizontal) //hstack

            Text(complieHash)
                .padding(.bottom, 40.0)

            if isLoading {
                ZStack {
                    Color(.white)
                        .ignoresSafeArea()
                        .opacity(0.9)

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

            Spacer()
        }

        .edgesIgnoringSafeArea(/*@[email protected]*/.all/*@[email protected]*/)
        .background(Color.white)


    }

    func startHolding(){
        isLoading = true

    }

    func startHoldingOff(){
        isLoading = false

    }

Figure 8-5 UI Buttons

9. Create a transaction

Contract account
On a contract account, the TEAL code must be compiled, to return an Algorand address. Before the user can perform the purchase, 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 transaction.
Note: Found your account Using Dispenser - TestNet Dispenser link

Create a new file named compile.teal and add the following code.
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

In the following example Teal Code, check these two Tutorials by Jason Weathersby, there well explained.

Introducing TEAL Version 3

Writing a Simple Smart Contract

// Opt-In Asset Amount is 0
txn AssetAmount
int 0
==
gtxn 1 Amount
int 10000
>=
return
txn Fee
int 10000
<=

EditorImages/2021/07/09 11:00/Screenshot_2021-07-09_at_11.32.12.png
Figure 9-1 Compile Teal

EditorImages/2021/06/20 17:31/10_1compile.png
Figure 9-2 Compile

Transaction
After TEAL code is compiled is approved, the user can now submit the transaction to the network using the buy button.

EditorImages/2021/06/20 17:32/10_2compile.png
Figure 9-3 Transaction

All Compile Teal code in step are in the ZCoffeeSwiftUIView.swift file.

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] = ZCoffeeSwiftUIView.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
    }

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

        var stxns:[SignedTransaction] = Array()

        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”

        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 account2 = try! Account(mnemonic2)
        let account = try! Account(mnemonic)
        let senderAddress = account.getAddress()
        let receiverAddress2 = account2.getAddress()

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

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

            let lsig = try! LogicsigSignature(logicsig: program)

            _ = try! account.signLogicsig(lsig: lsig)

            let tx = try? Transaction.paymentTransactionBuilder().setSender(senderAddress)
                .amount(1000000)
                .receiver(receiverAddress2)
                .note("draw algo with logic signature".bytes)
                .suggestedParams(params: paramResponse.data!)
                .build()

            let stx = Account.signLogicsigTransaction(lsig: lsig, tx: tx!)
            stxns.append(stx)

            let encodedTrans:[Int8]=CustomEncoder.encodeToMsgPack(stx)

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

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

                }else{
                    print(response.errorDescription!)
                    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

                if(confirmedRound != nil && confirmedRound! > 0){
                    print("confirmedRound")

                    self.transactionIsShowing = true
                    self.startHoldingOff()
                    if transactionIsShowing == true {
                        DispatchQueue.main.async {
                            print(transactionIsShowing)
                            self.assetRewardState = true

                        }
                    }
                }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;
            }
        }
    }


}

10. App Clip’s invocation URL

On the ContentView located in the folder ZCoffeeTarget add the following code to verify your App Clip’s invocation URL, and configure the domain’s server to provide the approval.

EditorImages/2021/06/20 18:16/5_12target.png
Figure 10-1 Add Navigation View

All UI and actions code in step are in the ContentView.swift file.

    var body: some View {
        NavigationView {
            ZCoffeeSwiftUIView()
        }

        .onContinueUserActivity(NSUserActivityTypeBrowsingWeb, perform: handleUserActivity)

}

func handleUserActivity(_ userActivity: NSUserActivity) {

    guard
        let incomingURL = userActivity.webpageURL,
        let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
        let _ = components.queryItems
    else {
        return
    }

    }

11. Execute and Check

After making an instant purchase, we get the result of our buy avoiding the need to download and install from the App Store the full App.

EditorImages/2021/06/20 18:20/11check.png
Figure 11-1 Order Placed

EditorImages/2021/06/20 19:09/transactionConfirmed.png
Figure 11-2 Confirmed

12. Full app UI

In this part of the tutorial, you can watch in Xcode project the full app part. It’s not relevant for this tutorial the full app, but you can add the UI as follow.

EditorImages/2021/06/20 18:33/12full.png
Figure 12-1 Full app UI

All code in this step is in the ContentView.swift file.

VStack(alignment: .center) {

            Image("logoZcoffee")

            Image("popular")
                .padding(.leading, -180.0)

            HStack {

                Image("brazilCoffee")
                Spacer()
                Image("mintCoffee")
                Spacer()
                Image("sumatraCoffee")

            }

            .padding(.all, 10.0)
            .frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width)

            Button(action: {

            }) {

                Image("more")

            }
            .padding(.leading, 250.0)

            Spacer()
            Image("bottom")

        }
        .edgesIgnoringSafeArea(/*@[email protected]*/.all/*@[email protected]*/)
        .frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width)
        .background((Color("backG")))

    }

13. Github Project

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

July 15, 2021