Skip to content

Commit

Permalink
ui: fees
Browse files Browse the repository at this point in the history
  • Loading branch information
reez authored Aug 23, 2023
1 parent 801fcd9 commit a8ff43f
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 5 deletions.
8 changes: 8 additions & 0 deletions BDKSwiftExampleWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
AE18E93A2A9532CB0019D2A4 /* BDKSwiftExampleWalletBundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE18E9392A9532CB0019D2A4 /* BDKSwiftExampleWalletBundle+Extensions.swift */; };
AE1C34242A424456008F807A /* ReceiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE1C34232A424456008F807A /* ReceiveView.swift */; };
AE1C34262A4248A5008F807A /* SendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE1C34252A4248A5008F807A /* SendView.swift */; };
AE2B8C1D2A9678C900815B2F /* FeeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE2B8C1C2A9678C900815B2F /* FeeService.swift */; };
AE2B8C1F2A96797300815B2F /* FeesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE2B8C1E2A96797300815B2F /* FeesResponse.swift */; };
AE3902A42A3B4CD900BEC318 /* TabHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE3902A32A3B4CD900BEC318 /* TabHomeView.swift */; };
AE49847C2A1BBBD6009951E2 /* BDKSwiftExampleWalletApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49847B2A1BBBD6009951E2 /* BDKSwiftExampleWalletApp.swift */; };
AE4984802A1BBBD7009951E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AE49847F2A1BBBD7009951E2 /* Assets.xcassets */; };
Expand Down Expand Up @@ -69,6 +71,8 @@
AE18E9392A9532CB0019D2A4 /* BDKSwiftExampleWalletBundle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BDKSwiftExampleWalletBundle+Extensions.swift"; sourceTree = "<group>"; };
AE1C34232A424456008F807A /* ReceiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiveView.swift; sourceTree = "<group>"; };
AE1C34252A4248A5008F807A /* SendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendView.swift; sourceTree = "<group>"; };
AE2B8C1C2A9678C900815B2F /* FeeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeService.swift; sourceTree = "<group>"; };
AE2B8C1E2A96797300815B2F /* FeesResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeesResponse.swift; sourceTree = "<group>"; };
AE3902A32A3B4CD900BEC318 /* TabHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabHomeView.swift; sourceTree = "<group>"; };
AE4984782A1BBBD6009951E2 /* BDKSwiftExampleWallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BDKSwiftExampleWallet.app; sourceTree = BUILT_PRODUCTS_DIR; };
AE49847B2A1BBBD6009951E2 /* BDKSwiftExampleWalletApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BDKSwiftExampleWalletApp.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -246,6 +250,7 @@
isa = PBXGroup;
children = (
AE7F67082A7451AA00CED561 /* PriceResponse.swift */,
AE2B8C1E2A96797300815B2F /* FeesResponse.swift */,
AE7F670B2A7451D700CED561 /* CurrencyCode.swift */,
AEB905C22A7EEBF000CD0337 /* BackupInfo.swift */,
);
Expand Down Expand Up @@ -274,6 +279,7 @@
AEB905C42A7EECAF00CD0337 /* Price Service */,
AEB905C12A7EEB9200CD0337 /* Key Service */,
AE1C34212A424434008F807A /* BDK Service */,
AE2B8C1C2A9678C900815B2F /* FeeService.swift */,
);
path = Service;
sourceTree = "<group>";
Expand Down Expand Up @@ -393,6 +399,8 @@
files = (
AE0C30F72A804A2D008F1EAE /* WalletTransactionListView.swift in Sources */,
AEB905C32A7EEBF000CD0337 /* BackupInfo.swift in Sources */,
AE2B8C1D2A9678C900815B2F /* FeeService.swift in Sources */,
AE2B8C1F2A96797300815B2F /* FeesResponse.swift in Sources */,
AE96F6672A4259260055623C /* QRCodeView.swift in Sources */,
AE7F670C2A7451D700CED561 /* CurrencyCode.swift in Sources */,
AE7F67052A7446B600CED561 /* PriceService.swift in Sources */,
Expand Down
16 changes: 16 additions & 0 deletions BDKSwiftExampleWallet/Model/FeesResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// FeesResponse.swift
// BDKSwiftExampleWallet
//
// Created by Matthew Ramsden on 8/23/23.
//

import Foundation

struct RecommendedFees: Codable, Equatable {
let fastestFee: Int
let halfHourFee: Int
let hourFee: Int
let economyFee: Int
let minimumFee: Int
}
21 changes: 21 additions & 0 deletions BDKSwiftExampleWallet/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@
}
}
}
},
"Economy Fee" : {

},
"Enter address to send BTC to" : {
"localizations" : {
Expand All @@ -121,6 +124,9 @@
}
}
}
},
"Fastest Fee" : {

},
"Fee %llu" : {
"localizations" : {
Expand All @@ -131,6 +137,12 @@
}
}
}
},
"Half Hour Fee" : {

},
"Hour Fee" : {

},
"Last Synced: %@" : {
"localizations" : {
Expand All @@ -141,6 +153,9 @@
}
}
}
},
"Minimum Fee" : {

},
"No Transactions" : {
"localizations" : {
Expand Down Expand Up @@ -192,6 +207,12 @@
}
}
}
},
"Select Fee" : {

},
"Selected fee: %lld satoshis" : {

},
"Send" : {
"localizations" : {
Expand Down
27 changes: 27 additions & 0 deletions BDKSwiftExampleWallet/Service/FeeService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// FeeService.swift
// BDKSwiftExampleWallet
//
// Created by Matthew Ramsden on 8/23/23.
//

import Foundation

struct FeeService {
func fees() async throws -> RecommendedFees {
guard let url = URL(string: "https://mempool.space/api/v1/fees/recommended") else { throw FeeServiceError.invalidURL }
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
200...299 ~= httpResponse.statusCode
else { throw FeeServiceError.invalidServerResponse }
let jsonDecoder = JSONDecoder()
let jsonObject = try jsonDecoder.decode(RecommendedFees.self, from: data)
return jsonObject
}
}

enum FeeServiceError: Error {
case invalidURL
case invalidServerResponse
case serialization
}
68 changes: 66 additions & 2 deletions BDKSwiftExampleWallet/View Model/SendViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,62 @@ import Observation
@MainActor
@Observable
class SendViewModel {
let feeService: FeeService
var balanceTotal: UInt64 = 0
var recommendedFees: RecommendedFees?
var selectedFeeIndex: Int = 2
var selectedFee: Int? {
guard let fees = recommendedFees else {
return nil
}
switch selectedFeeIndex {
case 0: return fees.minimumFee
case 1: return fees.hourFee
case 2: return fees.halfHourFee
default: return fees.fastestFee
}
}

var selectedFeeDescription: String {
guard let selectedFee = selectedFee else {
return "Failed to load fees"
}

let feeText = text(for: selectedFeeIndex)
return "Selected \(feeText) Fee: \(selectedFee) sats"
}

private func text(for index: Int) -> String {

switch index {

//"Minimum Fee"
case 0:
return "No Priority"

//"Hour Fee"
case 1:
return "Low Priority"

//"Half Hour Fee"
case 2:
return "Medium Priority"

//"Fastest Fee"
case 3:
return "High Priority"

default:
return ""

}

}

init(feeService: FeeService) {
self.feeService = feeService
}


func getBalance() {
do {
Expand All @@ -29,9 +84,18 @@ class SendViewModel {
do {
try BDKService.shared.send(address: address, amount: amount, feeRate: feeRate)
} catch let error as WalletError {
print("getBalance - Wallet Error: \(error.localizedDescription)")
print("send - Wallet Error: \(error.localizedDescription)")
} catch {
print("getBalance - Undefined Error: \(error.localizedDescription)")
print("send - Undefined Error: \(error.localizedDescription)")
}
}

func getFees() async {
do {
let recommendedFees = try await feeService.fees()
self.recommendedFees = recommendedFees
} catch {
print("getFees error: \(error.localizedDescription)")
}
}

Expand Down
24 changes: 22 additions & 2 deletions BDKSwiftExampleWallet/View/SendView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,31 @@ struct SendView: View {
.truncationMode(.middle)
.lineLimit(1)
}
VStack {
Picker("Select Fee", selection: $viewModel.selectedFeeIndex) {
Image(systemName: "gauge.with.dots.needle.0percent")
.tag(0)
Image(systemName: "gauge.with.dots.needle.33percent")
.tag(1)
Image(systemName: "gauge.with.dots.needle.50percent")
.tag(2)
Image(systemName: "gauge.with.dots.needle.67percent")
.tag(3)
}
.pickerStyle(.segmented) // TODO: use `.menu`

Text(viewModel.selectedFeeDescription)
}
}
.padding(.vertical, 50.0)
Button {
let feeRate: Float? = viewModel.selectedFee.map { Float($0) }
viewModel.send(
address: address,
amount: UInt64(amount) ?? UInt64(0),
feeRate: nil
feeRate: feeRate
)
// TODO: only if success clear out these fields?
amount = ""
address = ""
} label: {
Expand All @@ -85,12 +102,15 @@ struct SendView: View {
.buttonStyle(BitcoinOutlined(tintColor: .bitcoinOrange))
}
.padding()
.task {
await viewModel.getFees()
}

}

}
}

#Preview {
SendView(viewModel: .init())
SendView(viewModel: .init(feeService: .init()))
}
2 changes: 1 addition & 1 deletion BDKSwiftExampleWallet/View/TabHomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct TabHomeView: View {
)
}

SendView(viewModel: .init())
SendView(viewModel: .init(feeService: .init()))
.tabItem {
Label(
"Send",
Expand Down

0 comments on commit a8ff43f

Please sign in to comment.