Adding Wallet Functionalities
The page provides instructions on how to add wallet/Bolt functionalities to the sample app.
Last updated
The page provides instructions on how to add wallet/Bolt functionalities to the sample app.
Last updated
The following directories lies inside the xcode project. Here is the xcode screenshot for example:
Description for the directories and files are provided below:
--Bolt(directory) // Directory Containing all Wallet Functionalities
--App(directory)
--BoltHome.swift
--TransactionDetails.swift
--Model(directory) //Directory Containing Models that can be utilized by Bolt App Directory
--Balance.swift(file)
--Transaction.swift(file)
--Wallet.swift(file)
--View(directory) // Defininig View for Bolt/Wallet functionalities
--AvailableBalanceBlock.swift(file)
--WalletActionStack.swift(file)
--ViewModel(directory) // Directory Containing Models that can be utilized by View Directory
--BoltViewModel.swift(file)
//
// BoltHome.swift
// ZusExample
//
// Created by Aaryan Kothari on 29/12/22.
//
import SwiftUI
struct BoltHome: View {
@EnvironmentObject var boltVM: BoltViewModel
@State private var sortOrder = [KeyPathComparator(\Transaction.creationDate)]
var body: some View {
GeometryReader { gr in
ZStack(alignment: .bottom) {
VStack(alignment: .leading) {
AvailableBalanceBlock()
WalletActionStack(width: gr.size.width)
HStack(spacing: 15) {
Text("Transaction Hash")
Spacer()
Text("Transaction Date").layoutPriority(1)
Image(systemName: "chevron.right").opacity(0)
}
.bold()
ScrollView(showsIndicators: false) {
ForEach(Array(boltVM.transactions.sorted().enumerated()),id:\.offset) { index, txn in
NavigationLink(destination: TransactionDetails(transaction: txn)) {
TransactionRow(index: index, txn: txn)
}
}
.listStyle(.plain)
}
}
if boltVM.presentPopup {
ZCNToast(type: boltVM.popup,presented: $boltVM.presentPopup)
}
}
}
.padding(20)
.environmentObject(boltVM)
.navigationTitle(Text("Bolt"))
.navigationBarTitleDisplayMode(.large)
.onAppear(perform: boltVM.getTransactions)
.alert("Recieve ZCN", isPresented: $boltVM.presentReceiveView,actions: {recievAlert}) {
Text("Wallet address\n\(Utils.wallet?.client_id ?? "")")
}
.alert("Send ZCN", isPresented: $boltVM.presentSendView,actions: {sendAlert})
.alert("Error", isPresented: $boltVM.presentErrorAlert) {
Text(boltVM.alertMessage)
}
}
@ViewBuilder func TransactionRow(index:Int,txn:Transaction) -> some View {
HStack(spacing: 15) {
Text("\(index+1). \(txn.hash)")
Spacer()
Text(Date(timeIntervalSince1970: txn.creationDate/1e9).formatted()).layoutPriority(1)
Image(systemName: "chevron.right")
}
.foregroundColor(txn.status == 1 ? .primary : .pink)
.lineLimit(1)
.padding(.vertical,10)
}
@ViewBuilder var sendAlert: some View {
TextField("client ID", text: $boltVM.clientID)
TextField("amount", text: $boltVM.amount)
Button("Send",action:boltVM.sendZCN)
//.disabled(!boltVM.clientID.isValidAddress || !boltVM.amount.isValidNumber)
}
@ViewBuilder var recievAlert: some View {
Button("Copy",action:boltVM.copyClientID)
}
}
struct BoltHome_Previews: PreviewProvider {
static var previews: some View {
var boltVM: BoltViewModel = {
let boltVM = BoltViewModel()
boltVM.transactions = [Transaction(hash: "dhudhiduididg", creationDate: 93778937837837, status: 1),Transaction(hash: "dhudheijeioeiduididg", creationDate: 937789337837, status: 1),Transaction(hash: "dhudhidehieeuididg", creationDate: 9377893474837, status: 2)]
return boltVM
}()
BoltHome()
.environmentObject(boltVM)
}
}
Line 8 import SwiftUI
gives you access to SwiftUI-specific functionality . If you are writing UI Views you need to import SwiftUI.
Line 10 defines a BoltHome structure that helps us build the HomeUI of your Bolt(Wallet functionalities) code.
Line 11 Defines an Environment Object boltVM
in BoltHome structure for sharing data between many views in your app.@EnvironmentObject
property wrapper ensures views automatically stay updated when that data changes.
Line 12 defines a private variable sortOrder
with @State wrapper to hold state without losing it. SwiftUI can destroy and recreate our struct whenever needed. sortOrder is storing state Transaction.creationDate via KeyPathComparator that uses a sort comparator to provide the comparison of Transaction Creation values at a key path.
Line 13 defines the variable body
for holding the main view of BoltHome screen.
Line 14 utilizes GeometryReader from SwiftUI library imported above to calculate screen position and size information .
Line 15 to 42 builds app layouts with stack views. Individually, HStack
positions views in a horizontal line, VStack
positions them in a vertical line, and ZStack
overlays views on top of one another.
According to code, ZStack has its views on bottom with VStack defined inside it. View positioned vertically also includes available balance listed retrieved via AvailableBalanceBlock() method(Line 18).
WalletActionStack method is called at line 20 to provide relative sizes using GeometryReader for vertically positioned items.
Views for WalletApp that has to be positioned horizontally (HStack )
Line 28 bolds the whole text inside the HStack.
Line 29 defines a scrollable view within the scrollable content region .
Scroll indicators are set to false by default.
Line 30 defines what the scrollable view should contain which is for each sorted transaction by creation date list them as rows.
Line 43 to 53 defines the layout for the main View defined at Line 13
Padding for the main view is set to 20 pexels.
Environment Object boltVm is passed which holds the BoltViewModel instance.
Navigation Title which is set at top of app windows is named (Bolt)
App Title should be displayed in large texts.
.onAppear() adds an action(which is getting transaction details) before the main view appears.
Present an Alert for the user to receive ZCN
Print the Wallet Address by fetching the WalletClientID form Utils.swift
Present an alert for Sending ZCN and error(in case not able to send ZCN)
Line 57 to 67 defines a function for setting view of TransactionRow . The @ViewBuilder attribute is used with the function to create child views for a under the parent view which is BoltHome defined at line 10. The layout for transaction rows are as follows in HStack(Horizontally Stacked Views) : TransactionHash : Transaction Date Formatted
Line 69 to 76 defines variable sendAlert
for holding alerts with the @ViewBuilder attribute to create child views.
a) "some View” in means that the body will always be implementing the View protocol, but the implementation type does not need to be known by the caller.
b) Text field for Wallet clientID and amount is specified and the button is implemented for executing send ZCN token transactions.
Line 78 defines variable recieveAlert for a child view that defines a button for copying the WalletClientID.
Line 84 to 95 defines a Previewprovider type that produces view for the declared View structures .
a) Here we will pass our BoltHome
structure defined at line 10 to be available as a View preview.(Line 92)
b) At line 93 BoltHome
is passed with an environment object boltVM
for sharing data between many views in your app.
c)boltVM
is assigned a created BoltViewModel instance at line 87
d)At line 88 boltVM
populates the Transactions Array with transaction details accessible under BoltViewModel.swift.
//
// TransactionDetails.swift
// ZusExample
//
// Created by Aaryan Kothari on 07/01/23.
//
import SwiftUI
struct TransactionDetails: View {
var transaction: Transaction
var body: some View {
List {
Section("Signatur and Hashes") {
ListRow(title: "Transaction Hash:", value: transaction.hash)
ListRow(title: "Block Hash:", value: transaction.blockHash)
ListRow(title: "Output Hash:", value: transaction.outputHash)
ListRow(title: "Client ID:", value: transaction.clientID)
ListRow(title: "To Client ID:", value: transaction.toClientID)
ListRow(title: "Signature:", value: transaction.signature)
}
Section("Amount Details") {
ListRow(title: "Status:", value: transaction.status.stringValue)
ListRow(title: "Value:", value: transaction.value?.stringValue)
ListRow(title: "Fee:", value: transaction.fee?.stringValue)
ListRow(title: "Date:", value: transaction.fomattedDate)
}
Section("Explorer") {
Link(destination: URL(string: "https://staging-atlus-beta.testnet-0chain.net/transaction-details/\(transaction.hash)")!,label: { Text("View On Explorer").foregroundColor(.teal) })
}
}
.navigationTitle(Text("Transaction Details"))
}
@ViewBuilder func DictionarySection(title:String,value: String?) -> some View {
let dict = value?.json ?? [:]
Section(title) {
ForEach(Array(dict.keys).sorted(),id:\.self) { key in
ListRow(title: key, value: String(describing: dict[key]))
}
}
}
}
struct TransactionDetails_Previews: PreviewProvider {
static var previews: some View {
TransactionDetails(transaction: Transaction(createdAt: "", updatedAt: "", devaredAt: "", hash: "dgjydgjydgydgyjdgyddgjydgjydgydgyjdgyddgjydgjydgydgyjdgyd", blockHash: "duhdhdigdugdi", round: 2, version: "", clientID: "83738383763", toClientID: "397387387383", transactionData: "", value: 4, signature: "duhdujhdudh", creationDate: 2344, fee: 888, nonce: 88, transactionType: 2, transactionOutput: "djhdjhdj", outputHash: "djhdhidi", status: 2))
}
}
struct ListRow: View {
var title:String
var value: String?
@State var opened: Bool = false
var body: some View {
HStack {
Text(title)
Text(value ?? "~")
.lineLimit(10)
}
.onTapGesture {
withAnimation {
self.opened.toggle()
}
}
}
}
Line 8 import SwiftUI
gives you access to iOS UI-specific functionality . If you are writing SwiftUi View
you need to import SwiftUI.
Line 10 defines a structure TransactionDetails
which contains various properties and fields for creating view for transaction details.
Line 11 defines a transaction variable that points to Transaction Structure
Line 12 defines the variable body
for holding the main view of Transaction Details
Line 13 defines a List for displaying a collection of data
Inside that at line 14 ,to add a section around some cells, a Section
statement with title is added.
Line 15 to 20 will create rows under the list that holds Transaction data.
Line 23 to 28 again adds a section under the list with the rows that holds Transaction Amount Details (Status.Value,Fee,Date)
Line 30 to 33 adds another section in the list for Explorer under which a redirecting link to all-in -one network dashboard is listed to view Transaction details in more detail.
Line 34 defines the navigation title for TransactionDetails structure.
Line 37 to 45 defines a function DictionarySection
implementing a dictionary for holding transaction data that has to be populated in the list rows(Line 40 and 41) .
The @ViewBuilder attribute is used with the function to create child views under the parent view which is TransactionDetails struct defined at Line 10.
Line 47 to 51 defines a PreviewProvider type that produces view previews for the declared TransactionDetails struct .
Line 53 to 70 defines a structure on how how transaction list in terms of rows should be created.
According to the structure two variables are defined -- a variable for List title
is defined and an optional string variable value
that might hold or not hold value depending u[on the case .
Line 56 defines a boolean variable holding the toggle state which is set to false by default
Line 58 defines how views should be stacked horizontally.
First ,title
should be listed and then comes value
variable horizontally to that.(Line 60 and 61 )
The maximum number of lines that the text can occupy in a view is 10(Line 62)
Line 64 utilizes .onTapGesture
function which adds an action to perform when the view recognizes a tap gesture.
Line 65 utilizes withAnimation
function which returns the view’s body result with the defined animation which is self.opened.toggle() .
self
refers to the current object within the ListRow struct.
The toggle () function will switch the bool from true to false.
//
// Balance.swift
// ZusExample
//
// Created by Aaryan Kothari on 29/12/22.
//
import Foundation
import Zcncore
struct Balance: Codable, Equatable {
private var txn: String?
private var round: Int?
private var _balance: Int?
private var error: String?
internal init(txn: String? = nil, round: Int? = nil,balance _balance: Int? = nil, error: String? = nil) {
self.txn = txn
self.round = round
self._balance = _balance
self.error = error
}
enum CodingKeys: String, CodingKey {
case txn
case round
case _balance = "balance"
case error
}
var balance: Int {
return _balance ?? 0
}
var balanceToken: Double {
get { return balance.tokens }
set { self._balance = Int(exactly: ZcncoreConvertToValue(newValue).doubleValue) }
}
var usd: String {
let usd: Double = Utils.zcnUsdRate
let amount: Double = balanceToken * usd
let dollarString: String = "$ \(amount.rounded(toPlaces: 3))"
return dollarString
}
}
Describing Code:
Line 8 import Foundation
is required to provide a base layer of functionality for apps and frameworks, including data storage and persistence, text processing, date and time calculations, sorting and filtering, and networking.
Line 9 import Zcncore
is required to access Gosdk functions.
Line 11 defines a structure for how wallet balance should be retrieved. Codable means structure allows decoding data for custom types and encoding data to be saved or transferred.
Line 13 to 16 defines private variables in the struct for organizing balance details.
a)txn: holds transaction hash b)round : holds block rounds information c) balance : holds wallet balance d)error: hold balance error information
Line 18 to 22 internally initializes instance of a Balance structure .self
refers to the current object within the Balance structure.
Line 25 to 30 defines constants or enums(not changable variable) for the Balance structure.
Line 32 to 34 defines balance function which will return wallet balance as integer.
Line 36 to 39 defines balanceToken
function with a getter and setter methods.A getter method here allows access to private balance.tokens property, and where as setter method allows the private balance property to be set with a new value which is ZCN tokens to value of choice(Double) via ZcncoreConvertToValue
function.
Line 41 to 48 defines functionusd
for getting ZCN token balance in terms of USD
a) A usd
variable of type Double that gets value via Utils.zcnUsdRate function in Utils.swift.
b) A amount
variable of type Double that gets its value from balanceToken
at line 43
c) A dollarString
variable that returns ZCN token value in terms of USD.
//
// Transaction.swift
// ZusExample
//
// Created by Aaryan Kothari on 29/12/22.
//
import Foundation
typealias Transactions = [Transaction]
struct Transaction: Codable, Identifiable, Hashable,Comparable {
static func < (lhs: Transaction, rhs: Transaction) -> Bool {
return rhs.creationDate < lhs.creationDate
}
/// <#Description#>
/// - Parameters:
/// - id: <#id description#>
/// - createdAt: <#createdAt description#>
/// - updatedAt: <#updatedAt description#>
/// - devaredAt: <#devaredAt description#>
/// - hash: <#hash description#>
/// - blockHash: <#blockHash description#>
/// - round: <#round description#>
/// - version: <#version description#>
/// - clientID: <#clientID description#>
/// - toClientID: <#toClientID description#>
/// - transactionData: <#transactionData description#>
/// - value: <#value description#>
/// - signature: <#signature description#>
/// - creationDate: <#creationDate description#>
/// - fee: <#fee description#>
/// - nonce: <#nonce description#>
/// - transactionType: <#transactionType description#>
/// - transactionOutput: <#transactionOutput description#>
/// - outputHash: <#outputHash description#>
/// - status: <#status description#>
internal init(id: Int? = nil, createdAt: String? = nil, updatedAt: String? = nil, devaredAt: String? = nil, hash: String, blockHash: String = "", round: Int? = nil, version: String? = nil, clientID: String? = nil, toClientID: String? = nil, transactionData: String? = nil, value: Int? = nil, signature: String? = nil, creationDate: Double, fee: Int? = nil, nonce: Int? = nil, transactionType: Int? = nil, transactionOutput: String? = nil, outputHash: String? = nil, status: Int) {
self.id = id
self.createdAt = createdAt
self.updatedAt = updatedAt
self.devaredAt = devaredAt
self.hash = hash
self.blockHash = blockHash
self.round = round
self.version = version
self.clientID = clientID
self.toClientID = toClientID
self.transactionData = transactionData
self.value = value
self.signature = signature
self.creationDate = creationDate
self.fee = fee
self.nonce = nonce
self.transactionType = transactionType
self.transactionOutput = transactionOutput
self.outputHash = outputHash
self.status = status
}
var id: Int?
var createdAt, updatedAt: String?
var devaredAt: String?
var hash, blockHash: String
var round: Int?
var version, clientID, toClientID, transactionData: String?
var value: Int?
var signature: String?
var creationDate: Double
var fee, nonce, transactionType: Int?
var transactionOutput, outputHash: String?
var status: Int
enum CodingKeys: String, CodingKey {
case id = "ID"
case createdAt = "CreatedAt"
case updatedAt = "UpdatedAt"
case devaredAt = "DevaredAt"
case hash
case blockHash = "block_hash"
case round, version
case clientID = "client_id"
case toClientID = "to_client_id"
case transactionData = "transaction_data"
case value, signature
case creationDate = "creation_date"
case fee, nonce
case transactionType = "transaction_type"
case transactionOutput = "transaction_output"
case outputHash = "output_hash"
case status
}
var fomattedDate: String {
return Date(timeIntervalSince1970: self.creationDate/1e9).formatted()
}
}
Descriibing Code :
Line 8 import Foundation
is required to provide a base layer of functionality for apps and frameworks, including data storage and persistence, text processing, date and time calculations, sorting and filtering, and networking.
Line 12 defines a Transaction structure for app transactions.
Line 39 to 60 internally initializes instance of a Balance structure with variablesvariables .self
refers to the current instance within the Balance structure.
Line 62 to 73 defines optional variables( can hold None and Some) for holding various transaction details.
var id: Int? ///Transaction ID
var createdAt, updatedAt: String? //
var devaredAt: String?
var hash, blockHash: String ///Transaction Hash
var round: Int? ////Current Round Chain
var version, clientID, toClientID, transactionData: String? ///
var value: Int?
var signature: String? ///Wallet Signature
var creationDate: Double
var fee, nonce, transactionType: Int?
var transactionOutput, outputHash: String?
var status: Int //Transaction Status
Line 95 to 97 defines function for returning formattedDate that will be required in transaction information. self
refers to the current instance within the Balance structure.
//
// Wallet.swift
// ZusExample
//
// Created by Aaryan Kothari on 28/12/22.
//
import Foundation
struct Wallet: Codable {
let client_id: String
var client_key: String
var keys: [Keys]
var mnemonics: String
let version: String
func debugDescription() -> String {
return "\n----------Wallet----------\nclient_id: \(client_id)\nclient_key: \(client_key)\npublic_key: \(keys[0].public_key)\nprivate_key: \(keys[0].private_key)\nmnemonics: \(mnemonics)\nversion: \(version)\n--------------------------"
}
}
struct Keys: Codable {
let public_key: String
let private_key: String
}
Describing Code:
Line 8 import Foundation
is required to provide a base layer of functionality for apps and frameworks, including data storage and persistence, text processing, date and time calculations, sorting and filtering, and networking.
Line 10 defines a Wallet structure for getting wallet information in the app.
Line 11 to 15 defines variables in the wallet structure for holding wallet details.
a) client_id: holds wallet client_id b) client_key : holds wallet client_key c) keys : holds wallet public and private keys d) mnemonics: holds wallet mnemonics e) version : holds wallet version.
Line 17 to 20 defines a function for returning wallet information .
Line 22 to 25 defines Key structure for wallet public and private keys.
//
// AvailableBalanceBlock.swift
// ZusExample
//
// Created by Aaryan Kothari on 29/12/22.
//
import SwiftUI
struct AvailableBalanceBlock: View {
@EnvironmentObject var boltVM: BoltViewModel
@AppStorage(Utils.UserDefaultsKey.balance.rawValue) var balance: Int = 0
var body: some View {
VStack(alignment:.leading,spacing: 5) {
Text("Available Balance")
.font(.system(size: 14, weight: .regular))
HStack(alignment:.bottom,spacing:0) {
Text("\(balance.tokens.rounded(toPlaces: 3))")
.font(.system(size: 36, weight: .bold))
Text(" ZCN")
.font(.system(size: 14, weight: .regular))
.padding(.bottom, 8)
}
.padding(.top,-10)
HStack {
Text("Total Balance")
.font(.system(size: 16, weight: .regular))
Text("$ \(balance.usd)")
.font(.system(size: 16, weight: .bold))
}
Text("1 ZCN ≈ $\(Utils.zcnUsdRate)")
.foregroundColor(.secondary)
}
}
}
struct AvailableBalanceBlock_Previews: PreviewProvider {
static var previews: some View {
AvailableBalanceBlock()
.environmentObject(BoltViewModel())
.padding(20)
.previewLayout(.sizeThatFits)
}
}
Describing Code:
Line 8 import SwiftUI
gives you access to SwiftUI-specific functionality . If you are writing UI Views in your iOS app you need to import SwiftUI.
Line 10 defines a AvailableBalanceBlock
structure that helps us build the AvailableBalanceBlock in the Bolt(Wallet) App. <insert image here>
Line 11 defines a BoltViewModel variable boltVM
in for sharing data between various Bolt app views in your app(here ).@EnvironmentObject
property wrapper ensures views automatically stay updated when that data changes.
Line 12 defines a integer variable balance
with @AppStorage wrapper for reading values from UserDefaults
, The wrapper effectively watches a balance in UserDefaults
, and will refresh your UI if that key changes.
Line 14 defines the variable body
for holding the AvailableBalanceBlock view on the app screen.
Line 15 to 42 builds app layouts with stack views. Individually, HStack
positions views in a horizontal line, VStack
positions them in a vertical line.
a) Vertically positioned views (VStack ) are as follows: "Available Balance(text)" with spacing then amount of tokens fetched and rounded off from method in Balance.swift horizontally stacked. (Line 15 to 27)
b) More Horizontally positioned views (HStack) are as follows : Total Balance with token balance in ZCN then its ZCN equivalent in USD. (Line 29 to 41)
Line 43 to 50 defines a Previewprovider type that produces view for the declared AvailableBalanceBlock structure.
a) Here we will pass our AvailableBalanceBlock
structure defined at line 10 to be available as a View preview.(Line 45)
b) At line 46 BoltViewModel
is passed with an environment object for sharing data and access methods between views in your app.
c) 20 pixels for padding and spacing between previews.(Line47)
d)Set size constrains and layout for the preview according to the amount of space view requires(Line 48)
// WalletActionStack.swift
// ZusExample
//
// Created by Aaryan Kothari on 29/12/22.
//
import SwiftUI
struct WalletActionStack: View {
@EnvironmentObject var boltVM: BoltViewModel
@Environment(\.colorScheme) var colorScheme
var width: CGFloat
var body: some View {
HStack(spacing:0) {
ForEach(WalletActionType.allCases,id:\.self) { action in
WalletActionButton(width: width, action: boltVM.walletAction, button: action)
}
}
.frame(height:width/4)
.background(Color.tertiarySystemBackground)
.cornerRadius(12)
.shadow(color: .init(white: colorScheme == .dark ? 0.05 : 0.75), radius: 75, x: 0, y: 0)
.padding(.bottom,10)
}
}
struct WalletActionButton: View {
var width: CGFloat
var action: (WalletActionType)->()
var button: WalletActionType
init(width: CGFloat, action: @escaping (WalletActionType) -> (), button: WalletActionType) {
self.width = width
self.action = action
self.button = button
}
var body: some View {
VStack(spacing:15) {
Image(button.image)
.resizable()
.aspectRatio(1, contentMode: .fit)
.frame(width: width/13)
Text(button.title)
.font(.system(size: 13, weight: .semibold))
.foregroundColor(Color(uiColor: UIColor.label))
}
.frame(width: width/3)
.onTapGesture{ self.action(button) }
}
}
enum WalletActionType: CaseIterable {
case send
case receive
case faucet
var title: String {
switch self {
case .send: return "Send"
case .receive: return "Receive"
case .faucet: return "Faucet"
}
}
var image: String {
switch self {
case .send: return "send"
case .receive: return "receive"
case .faucet: return "faucet"
}
}
}
struct WalletActionStack_Previews: PreviewProvider {
static var previews: some View {
WalletActionStack(width: 345)
.padding(50)
.background()
.environmentObject(BoltViewModel())
.previewLayout(.sizeThatFits)
}
}
Describing Code:
Line 8 import SwiftUI
gives you access to SwiftUI-specific functionality . If you are writing UI Views in your iOS app you need to import SwiftUI.
Line 10 defines a WalletActionStack
structure that helps us define functionalities for the wallet (sending or receiving wallet tokens).
Line 11 defines a BoltViewModel variable boltVM
in for sharing data between main app and wallet stack view in your app(here ).@EnvironmentObject
property wrapper ensures views automatically stay updated when that data changes.
Line 12 creates a property that reads the color scheme of the current view using the key path of the colorScheme
property.
Line 13 defines a variable width
pointing to CGFloat for floating-point scalar values in Core Graphics and related frameworks.
Line 15 to 28 builds app layouts with stack views. Individually, HStack
positions views in a horizontal line,
a) Horizontally Stacked views (HStack ) are as follows: For each Wallet Action these are the following layouts:
Frame: Positions view within an invisible frame with the specified size. Background: System background uses the default background color based on light/dark mode. Corner Radius: Round the corners of a view by 12 points. Shadow: Adds a dark shadow to this view. Padding: Adds horizontal padding 10pt to specific edges of this view.
Line 30 implements a WalletActionButton structure which define properties for implementing buttons for wallet actions.
Line 31 defines a variable width
pointing to CGFloat for floating-point scalar values in Core Graphics and related frameworks.
A action variable which can switch between different WalletActionType.
A button variable which can holds buttons for different WalletActionType.
Line 35 to 39 initializes WalletActionButton instance with its properties.
Line 41 to 53 defines view layout for the WalletActionButton
a) VStack
positions views in a vertical line with the following properties. (Line 42)
b) A button image with .resizable() mode: Sets the mode by which SwiftUI resizes an image to fit its space.(Line 43 and 44)
c) .frame(width:width/13): Positions this view within an invisible frame with the specified size(width of window/13)(Line 46).
d) A button title with text and font size and weight specified(Line 47 and 48)
e) Total frame width for the complete button layout with title will be 1/3 of actual size .
f) Line 51 utilizes .onTapGesture
function which adds an action to perform when
the view recognizes a button click.
Line 55 to to 75 define Swift enumerations to store WalletActionType values which are send(send tokens),receive(receive tokens) and faucet(get tokens into wallet)
Line 60 to 66 uses switch control statements for flow of WalletActionTypes titles.
The switch
statement evaluates an expression inside paranthesis which is self
(WalletActionType Instance)(Line 61) . If the result of the expression is equal to a particular case that is (send,.receive,and .faucet) then statements for that particular case are executed.
Line 68 to 75 uses control statements for flow of WalletActionTypes images.
The switch
statement evaluates an expression inside paranthesis which is self
(WalletActionType Instance)(Line 69) . If the result of the expression is equal to a particular case that is (.send,.receive,and .faucet) then statements for that particular case are executed.
Line 77 to 83 defines a PreviewProvider type that produces view for the declared WalletActionStack structure.
a) Here we will pass our WalletActionStack
instance defined as a structure at line 10 to be available as a View preview.(Line 45)
b) At line 80 BoltViewModel
is passed as an environment object for sharing data and access methods between views in your app.
c) Set size constrains and layout for the preview according to the amount of space view requires(Line 81)
//
// BoltViewModel.swift
// ZusExample
//
// Created by Aaryan Kothari on 28/12/22.
//
import Foundation
import Zcncore
import Combine
import SwiftUI
class BoltViewModel:NSObject, ObservableObject {
@Published var balance: Double = 0.0
@Published var balanceUSD: String = "$ 0.00"
@Published var presentReceiveView: Bool = false
@Published var presentErrorAlert: Bool = false
@Published var presentSendView: Bool = false
@Published var alertMessage: String = ""
@Published var clientID: String = ""
@Published var amount: String = ""
@Published var transactions: Transactions = []
@Published var presentPopup: Bool = false
@Published var popup = ZCNToast.ZCNToastType.success("YES")
var cancellable = Set<AnyCancellable>()
override init() {
super.init()
Timer.publish(every: 30, on: RunLoop.main, in: .default)
.autoconnect()
.map { _ in }
.sink(receiveValue: getBalance)
.store(in: &cancellable)
if let balance = Utils.get(key: .balance) as? Int {
self.balance = balance.tokens
}
}
deinit {
NotificationCenter.default.removeObserver(self)
self.cancellable.removeAll()
}
func getBalance() {
var error: NSError? = nil
ZcncoreGetBalance(self, &error)
if let error = error { print(error.localizedDescription) }
}
func walletAction(_ action: WalletActionType) {
switch action {
case .send:
self.presentSendView = true
case .receive:
self.presentReceiveView = true
case .faucet:
self.receiveFaucet()
}
}
func receiveFaucet() {
self.presentSendView = false
DispatchQueue.global().async {
var error: NSError?
do {
DispatchQueue.main.async {
self.popup = .progress("Recieving ZCN from faucet")
self.presentPopup = true
}
let txObj = ZcncoreNewTransaction(self,"0",0,&error)
if let error = error { throw error }
try txObj?.executeSmartContract("6dba10422e368813802877a85039d3985d96760ed844092319743fb3a76712d3",
methodName: "pour",
input: "{}",
val: "10000000000")
} catch let error {
self.onTransactionFailed(error: error.localizedDescription)
}
}
}
func sendZCN() {
DispatchQueue.global(qos: .default).async {
do {
guard let amount = Double(self.amount) else {
self.onTransactionFailed(error: "invalid amount")
return
}
guard self.clientID.isValidAddress else {
self.onTransactionFailed(error: "invalid address")
return
}
guard !amount.isZero else {
self.onTransactionFailed(error: "amount cannot be zero")
return
}
guard amount <= self.balance else {
self.onTransactionFailed(error: "amount cannot be greater than balance")
return
}
guard Utils.wallet?.client_id != self.clientID else {
self.onTransactionFailed(error: "cannot send to own wallet")
return
}
DispatchQueue.main.async {
self.popup = .progress("Sending ZCN")
self.presentPopup = true
}
var error: NSError? = nil
let txObj = ZcncoreNewTransaction(self,"0",0,&error)
if let error = error { throw error }
try txObj?.send(self.clientID, val: ZcncoreConvertToValue(amount), desc: "")
DispatchQueue.main.async {
self.clientID = ""
self.amount = ""
}
} catch let error {
self.onTransactionFailed(error: error.localizedDescription)
}
}
}
func copyClientID() {
PasteBoard.general.setString(Utils.wallet?.client_key)
}
func getTransactions() {
DispatchQueue.global().async {
var error: NSError? = nil
let clientId = Utils.wallet?.client_id
ZcncoreGetTransactions(clientId, nil, nil, "desc", 20, 0, self , &error)
ZcncoreGetTransactions(nil, clientId, nil, "desc", 20, 0, self , &error)
if let error = error { print(error.localizedDescription) }
}
}
func onTransactionComplete(t: ZcncoreTransaction) {
DispatchQueue.main.async {
self.popup = .success("Success")
self.presentPopup = true
}
}
func onVerifyComplete(t: ZcncoreTransaction) {
}
func onTransactionFailed(error: String) {
DispatchQueue.main.async {
self.popup = .error(error)
self.presentPopup = true
}
}
}
extension BoltViewModel: ZcncoreGetBalanceCallbackProtocol {
func onBalanceAvailable(_ status: Int, value: Int64, info: String?) {
guard let response = info,
let data = response.data(using: .utf8),
let balance = try? JSONDecoder().decode(Balance.self, from: data) else {
return
}
Utils.set(value, for: .balance)
DispatchQueue.main.async {
self.balance = balance.balanceToken
self.balanceUSD = balance.usd
}
}
}
extension BoltViewModel: ZcncoreTransactionCallbackProtocol {
func onAuthComplete(_ t: ZcncoreTransaction?, status: Int) { }
func onTransactionComplete(_ t: ZcncoreTransaction?, status: Int) {
DispatchQueue.main.async {
let transaction = Transaction(hash: t?.getTransactionHash() ?? "", creationDate: Date().timeIntervalSince1970 * 1e9, status: status == 0 ? 1 : 2)
self.transactions.append(transaction)
self.objectWillChange.send()
}
guard status == ZcncoreStatusSuccess,
let txObj = t else {
self.onTransactionFailed(error: t?.getTransactionError() ?? "error: \(status)")
return
}
try? txObj.verify()
self.onTransactionComplete(t: txObj)
}
func onVerifyComplete(_ t: ZcncoreTransaction?, status: Int) {
self.getBalance()
}
}
extension BoltViewModel: ZcncoreGetInfoCallbackProtocol {
func onInfoAvailable(_ op: Int, status: Int, info: String?, err: String?) {
guard status == ZcncoreStatusSuccess,
let response = info,
let data = response.data(using: .utf8,allowLossyConversion: true) else {
print(err ?? "onInfoAvailable Error")
return
}
do {
if op == ZcncoreOpStorageSCGetTransactions {
let txns = try JSONDecoder().decode(Transactions.self, from: data)
var transactions = self.transactions
transactions.append(contentsOf: txns)
DispatchQueue.main.async {
self.transactions = Array(Set(transactions))
}
}
} catch let error {
print(error)
}
}
}
Describing Code:
Line 8 to 11 import libraries Foundation
is required to provide a base layer of functionality for apps and frameworks, including data storage and persistence, text processing, date and time calculations, sorting and filtering, and networking.
Zcncore
is required to access Gosdk functions.
SwiftUI
gives you access to SwiftUI-specific functionality . If you are writing UI Views in your iOS app you need to import SwiftUI.
Combine
gives handling of asynchronous events.
Line 13 BoltViewModel
class utilizes the zcncore libraries and provide the following functionalities for Bolt UI:
a) getBalance(): Retrieve Wallet Balance(Line 51 to 55)
b) recieveFaucet(): Receive tokens in wallet (Line 68 to 92)
c) sendZCN() : Send ZCN Tokens (Line 94 to 142)
d) copyClientID() : Copy Wallet ClientID(Line 144 to 146)
e) getTransactions(): Get Wallet Transactions(Line 148 to 158)
f) onTransactionComplete(): A function handling responses for successful transaction. (Line 160 to 165)
g) onTransactionFailed(): A function handling responses for failed transactions.
(Line 171 to 178)
ZcncoreGetBalanceCallbackProtocol() extends new functionality to the BoltViewModel structure with the onBalanceAvailable
method which return ZCN token balance in callback.(Line 180 to 193)
ZcncoreTransactionCallbackProtocol() extends new functionality to the BoltViewModel structure with onTransactionComplete
method which is called back when transaction is complete.(Line 198 to 214)
ZcncoreGetInfoCallbackProtocol() extends new functionality to the BoltviewModel structure with onInfoAvailable
method which is called back for transaction information. (Line 221 to 243)