Adding Storage Functionalities
The page provides instructions on how to add storage/vult functionalities to the app.
Last updated
The page provides instructions on how to add storage/vult functionalities to the 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:
--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
//
// 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 body
for 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.
//
// 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.
//
// 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.
//
// 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 .
//
// 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 .
//
// 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.
//
// 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.
//
// 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 allocation
for 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.