Adding Storage Functionalities

The page provides instructions on how to add storage/vult functionalities to the app.

Storage Directories

The following directories lies inside the xcode project. Here is the xcode screenshot for example:

Description for the directories and files are provided below:

--Vult(directory)
  --App(directory)
    --AllocationDetailsView.swift
    --VultHome.swift
  --Model(directory)
    --Allocations.swift
    --File.swift
  --View(directory)
    --AllocationActionStack.swift
    --AllocationDetailsBlock.swift
    --FilesTable.swift
    --ZCNProgressStyle.swift
  --ViewModel(directory)
    --VultViewModel.swift

Describing Code in Directories

Vult/App/VultHome.swift
//
//  VultHome.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 02/01/23.
//

import SwiftUI
import PhotosUI

struct VultHome: View {
    @EnvironmentObject var vultVM: VultViewModel
    
    var body: some View {
        GeometryReader { gr in
            VStack(alignment: .leading) {
                AllocationDetailsBlock()
                
                AllocationActionStack()
                
                FilesTable()
                                
                NavigationLink(destination: PreviewController(files: vultVM.files,file: vultVM.selectedFile).navigationTitle(Text(vultVM.selectedFile?.name ?? "")) .navigationBarTitleDisplayMode(.inline).navigationDocument(vultVM.selectedFile?.localThumbnailPath ?? URL(fileURLWithPath: ""))
,isActive: $vultVM.openFile) {
                    EmptyView()
                }
            }
            .padding(22)
        }
        .onAppear(perform: vultVM.listDir)
        .navigationTitle(Text("Vult"))
        .navigationBarTitleDisplayMode(.large)
        .background(Color.gray.opacity(0.1))
        .sheet(isPresented: $vultVM.presentAllocationDetails) { AllocationDetailsView(allocation: vultVM.allocation) }
        .fileImporter(isPresented: $vultVM.presentDocumentPicker, allowedContentTypes: [.image,.pdf,.audio],onCompletion: vultVM.uploadDocument)
        .onChange(of: vultVM.selectedPhoto, perform: vultVM.uploadImage)
        .environmentObject(vultVM)
    }
}

struct VultHome_Previews: PreviewProvider {
    static var previews: some View {
        let vm : VultViewModel = {
            let vm = VultViewModel()
            vm.files = [File(name: "IMG_001.PNG", mimetype: "", path: "", lookupHash: "", type: "", size: 8378378399, numBlocks: 0, actualSize: 0, actualNumBlocks: 0, encryptionKey: "", createdAt: 0.0, updatedAt: 0.0, completedBytes: 0)]
            return vm
        }()

        VultHome()
            .environmentObject(vm)
    }
}

Describing Code:

  • Line 8 import SwiftUI gives you access to SwiftUI-specific functionality . If you are writing UI Views you need to import SwiftUI.

  • Line 9 import PhotosUI framework for displaying a photo picker or choose photo from directory . Before using it, you have to first import the framework:

  • Line 10 defines a VultHome structure that helps us build the UI of your Vult(Storage functionalities) code.

  • Line 11 Defines a Environment Object vultVM in VultHome structure for sharing data between many views in your app.@EnvironmentObject property wrapper ensures views automatically stay updated when that data changes.

  • Line 14 defines the variable bodyfor holding the main view of VultHome screen.

  • Line 15 utilizes GeometryReader from SwiftUI library imported above to calculate screen position and size information .

  • Line 15 builds app layouts with stack views. Individually, VStack positions them in a vertical line.

  • Line 17 calls AllocationDetailsBlock() function in AllocationDetailsBlock.swift

  • Line 19 calls AllocationActionStack() function in AllocationActionStack.swift

  • Line21 calls FilesTable() function in FilesTable.swift

  • Line 23 implements a NavigationLink to push a view programmatically to navigation controller in this case user opens a file or navigates.

  • Line 25 calls an EmptyView() that doesn’t contain any content.

  • Line 28 adds a layer of padding to NavigationLink view defined at line 23 and 24.

  • Line 30 adds an action to list directories before the view in code snippet appears.

  • Line 31 defines title for the navigationLink which is Vult.

  • Line 32 configures the title display mode for the view to be large.

  • Line 33 defines a background in gray color.

  • Line 34 utilize Sheets in SwiftUI to present views(AllocationDetails) that partly cover the underlying screen.

  • Line 35 utilize fileImporter to presents a system interface for allowing the user to import multiple files and then call uploadDocument when the upload is completed.

  • Line 36 adds a modifier for the view and fires an action(uploading image) when the value changes (uploadImage).

  • Line 37 passes vultVM object as @EnvironmentObject* property wrapper to create and update views automatically.

  • Line 41 defines a Previewprovider type that produces view for the VultHome structure

  • Line 44 creates a variable vm to hold VultViewModel instance and access it methods

  • Line 45 specifies a file schema for upload

  • VultHome instance is passed as a view to be produced with environment object sharing data between VultViewModel Instance.

Vult/Model/Allocations.swift
//
//  Allocation.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 02/01/23.
//

import Foundation

typealias Allocations = [Allocation]

struct Allocation: Codable {
  var id,name: String?
  var dataShards, parityShards, expirationDate: Int?
  var size, usedSize, numOfWrites, numOfReads, totalChallenges: Int?
  var numOpenChallenges, numSuccessChallenges, numFailedChallenges: Int?
  var latestClosedChallenge: String?

  enum CodingKeys: String, CodingKey {
      case id
      case name
      case dataShards = "data_shards"
      case parityShards = "parity_shards"
      case size
      case expirationDate = "expiration_date"
      case usedSize = "used_size"
      case numOfWrites = "num_of_writes"
      case numOfReads = "num_of_reads"
      case totalChallenges = "total_challenges"
      case numOpenChallenges = "num_open_challenges"
      case numSuccessChallenges = "num_success_challenges"
      case numFailedChallenges = "num_failed_challenges"
      case latestClosedChallenge = "latest_closed_challenge"
  }
  
  mutating func addStats(_ model: Self) {
    self.usedSize = model.usedSize
    self.numOfWrites = model.numOfWrites
    self.numOfReads = model.numOfReads
    self.totalChallenges = model.totalChallenges
    self.numOpenChallenges = model.numOpenChallenges
    self.numSuccessChallenges = model.numSuccessChallenges
    self.numFailedChallenges = model.numFailedChallenges
    self.latestClosedChallenge = model.latestClosedChallenge
  }
    
    mutating func addSize(_ size: Int) {
        self.usedSize = (self.usedSize ?? 0) + size
    }
    
    var allocationFraction: Double {
        return Double(usedSize ?? 0)/Double(size ?? 1)
    }
    
    var allocationPercentage: Int {
        return ((usedSize ?? 0)/(size ?? 1)) * 100
    }
    
    var defaultName: String {
        if let name = self.name, !name.isEmpty {
            return name
        } else {
            return "Allocation"
        }
    }
}

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 makes use of a type alias allows you to provide a new name(Allocations in this case) for an existing data type (Allocation )into your program. The aliased name can be used instead of the existing type throughout the program.

  • Line 12 defines Allocation structure and the following fields :

    • var id : Allocation ID

    • var name : Allocation name

    • var dataShards : Holding data shards value, effects upload and download speeds

    • var parityShards: Holding parity shards value , effects availability

    • var expirationDate: Holding Allocation Expiry date

    • var used Size: Holds Used space in bytes inAllocation

    • var numofWrites : Holds value for number of times data written to allocation.

    • var numofReads : Holds value for number of times data read from allocations.

    • var totalChallenges : Holds Total Challenges for blobbers(opened+closed+successful+failed)

    • var numOpenChallenges : Holds value for number of open challenges blobber has to complete

    • var numSuccessChallenges : Holds value for number of challenges blobber successfully completed.

    • var numFailedChallenges : Holds value for number of failed challenges by blobbers.

    • var latestClosedChallenge :Holds value for latest completed challenge by blobber.

  • Line 19 to 34 created enumerations and cases for all the fields defined in Allocation Structure. A particular case can be executed depending upon the value of switch statement.

  • Line 36 to 45 defines a addStats function that's been marked as mutating and can change any property within its enclosing value(the values in this case that can be changed are from line 37 to 44)

  • Line 47 defines a mutating addSize function to modify the value of instance variable type allocation size.

  • Line 51 defines a allocationFraction variable which returns the fraction of allocation used.

  • Line 55 defines an allocationPercentage variable which returns allocation usedsize in terms of percentage.

  • Line 59 defines a variable defaultName which returns Allocation name retrieved from instance variable. In case the instance name field is empty return "Allocation" as String.

Vult/Model/File.swift
//
//  File.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 02/01/23.
//

import Foundation

struct Directory: Codable {
    let list: [File]
}

typealias Files = [File]

struct File: Codable, Identifiable, Equatable  {
    
    var id: String {
        return status.rawValue + completedBytes.stringValue
    }
    
    var name : String = ""
    var mimetype: String = ""
    var path: String = ""
    var lookupHash: String = ""
    var type: String = ""
    var size: Int = 0
    
    var numBlocks : Int? = 0
    var actualSize : Int? = 0
    var actualNumBlocks : Int? = 0
    var encryptionKey: String? = ""
    var createdAt: Double = 0
    var updatedAt: Double = 0
    
    enum CodingKeys: String, CodingKey {
        case name = "name"
        case mimetype = "mimetype"
        case path = "path"
        case lookupHash = "lookup_hash"
        case type = "type"
        case size = "size"
        case  numBlocks = "num_blocks"
        case  encryptionKey = "encryption_key"
        case  actualSize = "actual_size"
        case  actualNumBlocks = "actual_num_blocks"
        case  createdAt = "created_at"
        case  updatedAt = "updated_at"
    }
    
    internal init(name: String = "", mimetype: String = "", path: String = "", lookupHash: String = "", type: String = "", size: Int = 0, numBlocks: Int? = 0, actualSize: Int? = 0, actualNumBlocks: Int? = 0, encryptionKey: String? = "", createdAt: Double = 0, updatedAt: Double = 0, completedBytes: Int = 0) {
        self.name = name
        self.mimetype = mimetype
        self.path = path
        self.lookupHash = lookupHash
        self.type = type
        self.size = size
        self.numBlocks = numBlocks
        self.actualSize = actualSize
        self.actualNumBlocks = actualNumBlocks
        self.encryptionKey = encryptionKey
        self.createdAt = createdAt
        self.updatedAt = updatedAt
        self.completedBytes = completedBytes
    }
    
    var localThumbnailPath: URL {
      return Utils.downloadedThumbnailPath.appendingPathComponent(self.path)
    }
    
    var localUploadPath: URL {
      return Utils.uploadPath.appendingPathComponent(self.path)
    }
    
    var localFilePath: URL {
        return Utils.downloadPath.appendingPathComponent(self.path)
    }
    
    var isDownloaded: Bool {
      return FileManager.default.fileExists(atPath: localFilePath.path)
    }
    
     var isUploaded: Bool = true
    
     var completedBytes: Int = 0
    
    enum FileStatus {
        case error
        case progress
        case completed
    }
    
    var fileSize: String {
        switch status {
        case .completed: return size.formattedByteCount
        case .progress: return "\(completedBytes/size) %"
        case .error: return "failed"
        }
    }
    
    var fileDownloadPercent: String {
        return "\(completedBytes/size) %"
    }
    
     var status: FileStatus = .completed

}

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 directory strucure for files stored as list.

  • Line 14 makes use of a type alias allows you to provide a new name(Files in this case) for an existing data type (File )into your program. The aliased name can be used instead of the existing type throughout the program.

  • Line 16 defines File structure and the following fields :

    • var id : File ID .

    • var name : File name

    • var mimetype : File MIME Type

    • var path: File path

    • var lookuphash: Holding File LookupHash

    • var type : Holding File Type

    • var size : Holding value for File Size

    • var numBlocks : Holding value file in terms of number of blocks .

    • var actualSize : Holding Actual File Size.

    • var actualNumBlocks : Holding value for file in terms of actual file blocks.

    • var encryption : Holds value for file encrpytion Key.

    • var createdAt : Holding value for when file is created

    • var updatedAt :Holding value for when file is updated.

  • Line 36 to 65 created enumerations and cases for all the fields defined in File Structure. A particular case can be executed depending upon the value of particular statement evaluation in the program.

  • Line 67 to 81 creates the following variable for handling various file operations.

    • localThumbnailPath: Returns thumbnail path for the file.

    • localUploadPath: Returns local path for the uploaded file

    • localFilePath : Returns Local path for the file.

    • isDownloaded : Returns whether the downloaded file exists at specified path.

    • Boolean isUploaded : Returns boolean value true when the file is uploaded

    • completedBytes : Returns number of completed bytes for file download.

  • Line 87 define enum and cases for file status.

  • Line 91 defines variable fileSize and functionalities for cases created for file status

    • In case of completed file status return the file size in bytes.

    • In case of file status in progress return number of completed bytes divide by size in percentage.

    • In case of file status in error ,return failed status.

  • Line 93 to 99 defines variable fileDownloadPercent which returns downloaded file progress in percent.

Vult/View/AllocationActionStack.swift
//
//  AllocationActionStack.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 03/01/23.
//

import SwiftUI
import PhotosUI

struct AllocationActionStack: View {
    @EnvironmentObject var vultVM: VultViewModel
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        HStack(spacing:10) {
            
            PhotosPicker(
                selection: $vultVM.selectedPhoto,
                matching: .images,
                photoLibrary: .shared()) {
                    WalletActionBlock(icon: "photo",title: "Upload Image")
                }
            
            WalletActionBlock(icon: "document",title: "Upload Document")
                .onTapGesture {
                    vultVM.presentDocumentPicker = true
                }
        }
        .aspectRatio(3.2, contentMode: .fit)
        .shadow(color: .init(white: colorScheme == .dark ? 0.05 : 0.95), radius: 100, x: 0, y: 0)
    }
}

struct AllocationActionStack_Previews: PreviewProvider {
    static var previews: some View {
        AllocationActionStack()
            .environmentObject(VultViewModel())
            .background(Color.gray.opacity(0.1))
            .previewLayout(.sizeThatFits)
    }
}

struct WalletActionBlock: View {
    var icon: String
    var title: String
    
    var body: some View {
        GeometryReader { gr in
            VStack(alignment: .center) {
                Image(icon)
                    .resizable()
                    .aspectRatio(1, contentMode: .fit)
                    .frame(width: gr.size.width/2)
                Text(title)
            }
            .frame(maxWidth: .infinity,maxHeight: .infinity)
            .font(.system(size: 13, weight: .semibold))
            .foregroundColor(.primary)
            .padding()
            .background(Color.tertiarySystemBackground)
            .cornerRadius(12)
        }
    }
}

Describing Code :

  • Line 8 import SwiftUI gives you access to SwiftUI-specific functionality . If you are writing UI Views you need to import SwiftUI.

  • Line 9 import PhotosUI framework for displaying a photo picker or choose photo from directory . Before using it, you have to first import the framework

  • Line 11 defines a AllocationAction structure that helps us manage view for the Allocation functionalities in Vult code.

  • Line 12 defines a Environment Object vultVM in pointing to VultViewModel structure for sharing data between views in your app.@EnvironmentObject property wrapper ensures views automatically stay updated when that data changes.

  • Line 13 creates variable that holds value for possible color schemes, corresponding to the light and dark appearances.

  • Line 15 to 32 builds app layouts with stack views. Individually, HStack positions them in a horizontal line.

    • Line 17 to 20 implements a view that displays a Photos picker for choosing assets from the photo library.

    • Line 21 appends the photopicker with a WalletActionBlock.

    • Line 24 to 27 creates another WalletActionBlock named Upload Document which presents a document picker for choosing documents from library when ontap gesture is detected.

  • Line 34 to 40 defines a Previewprovider type that produces view for the AllocationActionStack structure .An AllocationActionStack instance is passed as a view to be produced.

  • Line 43 to 63 defines view for the WalletActionBlock utilized by AllocationActionBlock

  • Line 44 and 45 defines the following variables for WalletActionBlock.

    • A variable named icon for holding photo.

    • A variable named title for naming particular action functionality uploading documents or photos.

  • Line 48 defines a GeometryReader from SwiftUI library imported above to calculate screen position and size information.

  • Line 50 to 54 builds app layouts with stack views. Individually, VStack positions them in a vertical line .

Vult/View/AllocationDetailsBlock.swift
//
//  AllocationDetailsBlock.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 03/01/23.
//

import SwiftUI

struct AllocationDetailsBlock: View {
    @EnvironmentObject var vultVM: VultViewModel
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
            HStack(spacing:20) {
                VStack(alignment: .leading) {
                    Text(vultVM.allocation.defaultName)
                        .font(.system(size: 14, weight: .semibold))
                    Text(vultVM.allocation.expirationDate?.formattedUNIX ?? "")
                        .font(.system(size: 12, weight: .semibold))
                        .foregroundColor(.gray)
                }
                
                VStack(alignment: .leading) {
                    ProgressView(value: vultVM.allocation.allocationFraction)
                        .progressViewStyle(ZCNProgressStyle())
                        .frame(height: 10)
                    
                    Text("\(vultVM.allocation.usedSize?.formattedByteCount ?? "") used of \(vultVM.allocation.size?.formattedByteCount ?? "") (\(vultVM.allocation.allocationPercentage)%)")
                        .font(.system(size: 12, weight: .semibold))
                        .foregroundColor(.gray)
                        .lineLimit(1)
                }
                
        }
        .padding(.horizontal,16)
        .padding(.vertical,12)
        .background(Color.tertiarySystemBackground)
        .cornerRadius(12)
        .shadow(color: .init(white: colorScheme == .dark ? 0.25 : 0.75), radius: 100, x: 0, y: 0)
    }
}

struct AllocationDetailsBlock_Previews: PreviewProvider {
    static var previews: some View {
        AllocationDetailsBlock()
            .environmentObject(VultViewModel())
            .previewLayout(.sizeThatFits)
    }
}

Describing Code :

  • Line 8 import SwiftUI gives access to SwiftUI-specific functionality . If you are writing UI Views you need to import SwiftUI.

  • Line 10 defines a AllocationDetailsBlock structure that defines how allocation details should be displayed in the App.

    • Line 15 to 22 builds app layouts with stack views. Individually, VStack positions them in a vertical line(top to bottom) .HStack, a horizontal stack, which shows views in a left-to-right list.

    • Allocation Name and Expiration Date is listed as a textView.(Line 16 to 21)

    • Then in another Vertical Stack(VStack) a progress view and text is defined describing how much allocation space is used by files (Line 24 to 32)

  • Line 36 to 40 defines padding,background and shadow for Vertically and Horizontally Stacked Views.

  • Line 44 to 44 defines a Previewprovider type that produces view for the AllocationDetailsBlock structure .An AllocationDetailsBlock() instance is passed with VultViewModel instance as an environment object to share data during view production .

Vult/View/FilesTable.swift
//
//  FilesTable.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 03/01/23.
//

import SwiftUI

struct FilesTable: View {
    @EnvironmentObject var vultVM: VultViewModel
    @State var previewFile:Bool = false
    var body: some View {
        VStack(alignment: .leading) {
            Text("All Files").bold()
            
           ScrollView(showsIndicators: false) {
                ForEach(vultVM.files,id:\.id) { file in
                   FileRow(file: file)
                    .id(file.id)
                    .onTapGesture {
                        if file.isDownloaded {
                            self.vultVM.openFile = true
                            self.vultVM.selectedFile = file
                        } else if file.isUploaded {
                            vultVM.downloadImage(file: file)
                        }
                    }
                }
            }
        }
    }
}

struct FilesTable_Previews: PreviewProvider {
    static var previews: some View {
        let vm : VultViewModel = {
            let vm = VultViewModel()
            vm.files = [File(name: "IMG_001.PNG", mimetype: "", path: "", lookupHash: "", type: "", size: 8378378399, numBlocks: 0, actualSize: 0, actualNumBlocks: 0, encryptionKey: "", createdAt: 0.0, updatedAt: 0.0, completedBytes: 0)]
            return vm
        }()
        
        FilesTable()
            .environmentObject(vm)
    }
}

struct FileRow: View {
    @State var file: File
    var body: some View {
        HStack(spacing: 20) {
            if let image = ZCNImage(contentsOfFile: file.localThumbnailPath.path) {
                Image(image)
                    .resizable()
                    .frame(width: 40, height: 40)
                    .cornerRadius(8)
            } else {
                Image(systemName: "doc.circle.fill")
                    .resizable()
                    .symbolRenderingMode(.hierarchical)
                    .frame(width: 38, height: 38)
                    .cornerRadius(8)
                    .foregroundColor(.teal)
            }
            
            Text(file.name)
                .font(.system(size: 15, weight: .semibold))
                .lineLimit(1)
            
            Spacer()
            
            Text(file.fileSize)
                .font(.system(size: 14, weight: .semibold))
                .foregroundColor(.gray)
            
            if !file.isDownloaded && file.isUploaded {
                VStack(alignment: .center,spacing: 3) {
                    Image(systemName: "arrow.down.to.line.circle")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(height: 20)
                        .symbolRenderingMode(file.status == .progress ? .monochrome : .hierarchical)
                    
                    if file.status == .progress {
                        Text(file.fileDownloadPercent)
                            .font(.system(size: 8))
                    }
                }
                .foregroundColor(.teal)
            }
        }
        .padding(.vertical,12)
        .padding(.horizontal,18)
        .background(Color.tertiarySystemBackground)
        .cornerRadius(12)
    }
}

Describing Code :

  • Line 8 import SwiftUI gives access to SwiftUI-specific functionality . If you are writing UI Views you need to import SwiftUI.

  • Line 10 defines a FilesTable structure that dictates how files uploaded to dStorage should be displayed in the App.

    • Line 11 defines VultVM variable that holds a VultViewModel object . @EnvironmentObject property wrapper ensure views are shared properly across an entire SwiftUI app.

    • Line 12 defines a Boolean for holding state in case when the file is previewed

    • Line 14 to 33 builds app layouts with stack views. Individually, VStack positions them in a vertical line(top to bottom) with a text view titled(All Files) in bold

    • Line 17 implements a scrollable view for all the files on the Allocation

    • Line 18 to 20 list all the files available in the allocation

    • Line 21 to 27 defines action in case of tap on a listed file or download button.

      Flow: If user downloads the file open the selected file. else if the file is uploaded download the selected or tapped file

  • Line 35 to 46 defines a Previewprovider type that produces view for the FilesTable structure .A FilesTable() instance is passed with VultViewModel instance as an environment object to share data during view production .

  • Line 48 to 95 defines a structure that dictates the layout for the listed files

    • (Line 49) A variable file holding a particular file state.

    • (Line 51) HStack, a horizontal stack, which shows views in a left-to-right list with spacing of 20 pexels.

    • (Line 53 to 56) In a HStack properties for the displayed image are defined(Image has to be resizable with a specified frame and corner radius )

    • (Line 58 to 63) In case no thumbnail found for a particular image the thumbnail image should have the following properties.

      • A resizable thumbnail image of teal color with a specified frame and corner radius

    • (Line 66 to 74 ) A view that displays file name as text and size with display properties

    • (Line 76 to 89)If the Uploaded files are downloaded display the following symbol..

    • Line (92 to 95) defines padding,background and radius for Vertically and Horizontally Stacked Views.

Vult/View/ZCNProgressStyle.swift
//
//  ZCNProgressStyle.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 03/01/23.
//

import Foundation
import SwiftUI

struct ZCNProgressStyle: ProgressViewStyle {
    var strokeColor = Color.blue
    var strokeWidth = 25.0
    
    func makeBody(configuration: Configuration) -> some View {
        let fractionCompleted = configuration.fractionCompleted ?? 0
        
        return ZStack {
            GeometryReader { gr in
                RoundedRectangle(cornerRadius: gr.size.height/2)
                    .fill(Color.teal.opacity(0.3))
                    .frame(width: gr.size.width, alignment: .leading)
                RoundedRectangle(cornerRadius: gr.size.height/2)
                    .fill(Color.teal)
                    .frame(width: gr.size.width * fractionCompleted, alignment: .leading)
            }
        }
    }
}

Describing Code :

  • Line 11 defines a ZCNProgressStyle structure that dictates the layout for the ZCN progress bar in case of faucet send and recieve.

  • Line 15 defines function makeBody to define how the layout should be for the ZCN progress bar.

  • Line 16 defines a variable that holds ZCN progress in fractions

  • Line 18 to 25 defines a ZStack, a depth-based stack, which shows views in a back-to-front list defines a rounded rectangle inn which ZCN progress bar in fractions should be displayed.

Vult/ViewModel/VultViewModel.swift
//
//  VultViewModel.swift
//  ZusExample
//
//  Created by Aaryan Kothari on 28/12/22.
//

import Foundation
import Zcncore
import Combine
import _PhotosUI_SwiftUI

class VultViewModel: NSObject, ObservableObject {
    static var zboxAllocationHandle : ZboxAllocation? = nil
    
    @Published var allocation: Allocation = Allocation()
    @Published var presentAllocationDetails: Bool = false
    @Published var presentDocumentPicker: Bool = false

    @Published var files: Files = []
    @Published var selectedPhoto: PhotosPickerItem? = nil

    @Published var selectedFile: File? = nil
    @Published var openFile: Bool = false

    override init() {
        super.init()
        VultViewModel.zboxAllocationHandle = try? ZcncoreManager.zboxStorageSDKHandle?.getAllocation(Utils.get(key: .allocationID) as? String)
        self.getAllocation()
    }
    
    func getAllocation()  {
        DispatchQueue.global().async {
            do {
                let decoder = JSONDecoder()
                
                guard let zboxAllocationHandle = VultViewModel.zboxAllocationHandle else { return }
                
                var allocation = Allocation()
                allocation.id = zboxAllocationHandle.id_
                allocation.size = Int(zboxAllocationHandle.size)
                allocation.dataShards = zboxAllocationHandle.dataShards
                allocation.parityShards = zboxAllocationHandle.parityShards
                allocation.name = zboxAllocationHandle.name
                allocation.expirationDate = Int(zboxAllocationHandle.expiration)
                
                let allocationStats = zboxAllocationHandle.stats
                let allocationStatsData = Data(allocationStats.utf8)
                let allocationStatsModel = try decoder.decode(Allocation.self, from: allocationStatsData)

                allocation.addStats(allocationStatsModel)
                
                DispatchQueue.main.async {
                    self.allocation = allocation
                }
                
            } catch let error {
                print(error)
            }
        }
    }
    
    func uploadImage(selectedPhoto: PhotosPickerItem?) {
        Task {
            do {
                guard let newItem = selectedPhoto else { return }
                let name = PHAsset.fetchAssets(withLocalIdentifiers: [newItem.itemIdentifier!], options: nil).firstObject?.value(forKey: "filename") as? String ?? ""
                if let data = try await newItem.loadTransferable(type: Data.self) {
                    try uploadFile(data: data, name: name)
                }
            } catch let error {
                print(error.localizedDescription)
            }
        }
    }
    
    func uploadDocument(result: Result<URL, Error>) {
        do {
            let url = try result.get()
            guard url.startAccessingSecurityScopedResource() else { return }
            let name = url.lastPathComponent
            let data = try Data(contentsOf: url)
            try uploadFile(data: data, name: name)
        } catch let error {
            print(error.localizedDescription)
        }
    }
    
    func uploadFile(data: Data, name: String) throws {
                
        var localPath = Utils.uploadPath.appendingPathComponent(name)
        var thumbnailPath =  Utils.downloadedThumbnailPath.appendingPathComponent(name)
        
        if let image = ZCNImage(data: data) {
            let pngData = image.pngData()
            try pngData?.write(to: localPath,options: .atomic)
            
            let thumbnailData = image.jpegData(compressionQuality: 0.1)
            try thumbnailData?.write(to: thumbnailPath)
        } else {
            try data.write(to: localPath,options: .atomic)
        }
        
        try VultViewModel.zboxAllocationHandle?.uploadFile(withThumbnail: Utils.tempPath(),
                                                           localPath: localPath.path,
                                                           remotePath: "/\(name)",
                                                           fileAttrs: nil,
                                                           thumbnailpath: thumbnailPath.path,
                                                           statusCb: self)
    }
    
    func downloadImage(file: File) {
        do {
            try VultViewModel.zboxAllocationHandle?.downloadFile(file.path,
                                                                 localPath: file.localFilePath.path,
                                                                 statusCb: self)
        } catch let error {
            print(error.localizedDescription)
        }
    }
    
    func listDir() {
        DispatchQueue.global().async {
            do {
                guard let allocation = VultViewModel.zboxAllocationHandle else { return }
                
                var error: NSError? = nil
                let jsonStr = allocation.listDir("/", error: &error)
                
                if let error = error { throw error }
                
                guard let data = jsonStr.data(using: .utf8) else { return }
                
                let files = try JSONDecoder().decode(Directory.self, from: data).list
                
                DispatchQueue.main.async {
                    self.files = files
                }
                
            } catch let error {
                print(error.localizedDescription)
            }
        }
    }
    
}

extension VultViewModel: ZboxStatusCallbackMockedProtocol {
    func commitMetaCompleted(_ request: String?, response: String?, err: Error?) {
        
    }
    
    func completed(_ allocationId: String?, filePath: String?, filename: String?, mimetype: String?, size: Int, op: Int) {
        print("completed \(filePath) \(size.formattedByteCount)")
        DispatchQueue.main.async {
            if let index = self.files.firstIndex(where: {$0.path == filePath}) {
                self.files[index].completedBytes = size
                self.files[index].status = .completed
                if op == 0 {
                    self.files[index].isUploaded = true
                }
            }
        }
        DispatchQueue.main.async {
            self.allocation.addSize(size)
        }
    }
    
    func error(_ allocationID: String?, filePath: String?, op: Int, err: Error?) {
        print("error \(filePath) \(err?.localizedDescription)")
        DispatchQueue.main.async {
            if let index = self.files.firstIndex(where: {$0.path == filePath}) {
                self.files[index].status = .error
            }
        }
    }
    
    func inProgress(_ allocationId: String?, filePath: String?, op: Int, completedBytes: Int, data: Data?) {
        print("inProgress \(filePath) \(completedBytes.formattedByteCount)")
        DispatchQueue.main.async {
            if let index = self.files.firstIndex(where: {$0.path == filePath}) {
                self.files[index].completedBytes = completedBytes
                self.files[index].status = .progress
                self.objectWillChange.send()
            } else {
                var file = File()
                file.path = filePath ?? ""
                file.name = filePath?.replacingOccurrences(of: "/", with: "") ?? ""
                file.completedBytes = completedBytes
                file.status = .progress
                file.isUploaded = false
                DispatchQueue.main.async {
                    self.files.append(file)
                }
            }
        }
    }
    
    func repairCompleted(_ filesRepaired: Int) {
        
    }
    
    func started(_ allocationId: String?, filePath: String?, op: Int, totalBytes: Int) {
        print("started \(filePath) \(totalBytes.formattedByteCount)")
        if op == 0 {
            var file = File()
            file.path = filePath ?? ""
            file.name = filePath?.replacingOccurrences(of: "/", with: "") ?? ""
            file.size = totalBytes
            file.completedBytes = 0
            file.status = .progress
            file.isUploaded = false
            DispatchQueue.main.async {
                self.files.insert(file, at: 0)
            }
        }
    }
    
}

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 10 import Combine is required to handle asynchronous events.

  • Line 11 import PhotosUI framework for displaying a photo picker or choose photo from directory . Before using it, you have to first import the framework:

  • Line 13 defines a VultViewModel structure that utilizes the zcncore libraries and provide the following functionalities for Vult UI:

    • Line 14 Inside the structure a variable zboxAllocationHandle is created to handle ZBoxAllocation object.

    • @Published is one of the most useful property wrappers in SwiftUI, allowing us to create observable objects that automatically announce when changes occur.

    • Line 16 defines the variable allocationfor holding a Allocation type

    • Line 17 defines a boolean which holds state for whether AllocationDetails are present.

    • Line 18 defines a boolean which holds state for whether document picker to upload document is active.

    • Line 20 defines variable files for holding an array of files

    • Line 21 defines variable that holds value for the current selected photo in the UI

    • Line 23 defines variable selectedFile to hold value for a selected file in the UI

    • Line 24 defines a boolean variable to hold state of opened files.

    • Line 26 to 36 defines a constructor which initializes a VultViewModel instance and creates/retrieves an allocation on dStorage via gosdk getAllocation function.

  • Line 32 defines getAllocation()function to retrieve allocation details .

    Following Allocation details are retrieved utilizing gosdk zboxAllocation library

    • Allocation ID(Line 40)

    • Allocation Size(Line 41)

    • Allocation Data Shards(Line 42)

    • Allocation Parity Shards)(Line 43)

    • Allocation Name(Line 44)

    • Allocation ExpirationDate(Line 45)

  • Line 51 calls addStats function in Allocation.swift

  • Line 52 assigns all the allocation details to VultViewModel instance allocation variable.

  • Line 63 defines function uploadImage that provides functionality for uploading an image to the UI.

    • utilizes a Swiftui Photos picker view to select media assets .(Line63)

    • Using a new variable newItem to hold selected media asset.(line 66)

    • Fetching the file name from media assets and assigning those values to name variable.

    • At line 68 the data variable holds the selected media asset and uploads it to dStorage using [gosdk uploadFile] function.

    • A catch statment with error is defined at line 71 in case above functionalities does not work as expected .

  • Line 77 defines a function uploadDocument for uploading document via Vult App

    • A Result special type is specified that allows to encapsulate a successful value or some kind of error type in a single piece of data.(Line 77)

    • A url variable holds all the doc data retrieved via Result special type.

    • At line 82 and 83 the data variable holds the selected doc asset and uploads it using uploadFile function.

  • Line 89 defines a function uploadFile taking the following parameters.

    • data : Data --> A single piece of data

    • name: name --> File Name.

    • Line 91 and 92 creates variable localPath and thumbnailPath that holds value for the local file path and file thumbnail path.The paths are retrieved using Utils structure.

    • line 94 to 99 retrieve data from Data block and writes them to localPath and thumbnailPath.

    • Line 104 to 110 utilize gosdk uploadFile function and pass the necessary parameters to upload file to dStorage network.

      • localPath: Provide the local File Path to upload file from.

      • remotePath : Provide a path to upload file to decentralized remote storage.

      • thumbnailpath : Provide thumbnail path of the uploaded file.

      • statusCb : Does all the checks whether file has uploaded successfully.

      • withThumbnail: Retrieves the path of Thumbnail via Utils structure method.

  • Line 112 to 120 defines a function downloadImage that utilize gosdk downloadFile function for downloading file to dStorage

  • Line 122 to 144 defines a function listDir that will list all the directories in the allocation.

  • Line 148 ZboxStatusCallbackProtocol() extends new functionality to the VultVIewModel structure with the following callback functions :

    • (Line 153 to 167) func completed : Displays output when file upload operations are completed successfully

    • (Line 169 to 176) func error : Displays error output when file operations encountered an error.

    • (Line 178 to 197)func inProgress : Displays progress of file upload operations .

    • (Line 203 to 217) func started: Displays whether file operations have started.

Last updated