Implement transaction editing and deleting

Signed-off-by: Billy Brawner <billy@wbrawner.com>
This commit is contained in:
Billy Brawner 2019-10-16 19:26:35 -07:00
parent ad2fb25e9d
commit df9cec6cf7
11 changed files with 426 additions and 135 deletions

View file

@ -7,7 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
2821266023555FD300072D52 /* EditTransactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2821265F23555FD300072D52 /* EditTransactionView.swift */; };
2821266023555FD300072D52 /* EditTransactionForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2821265F23555FD300072D52 /* EditTransactionForm.swift */; };
282126622357E45F00072D52 /* TransactionEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 282126612357E45F00072D52 /* TransactionEditView.swift */; };
284102252341998300EAFA29 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 284102242341998300EAFA29 /* ContentView.swift */; };
2841022723419A2B00EAFA29 /* TabbedBudgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2841022623419A2B00EAFA29 /* TabbedBudgetView.swift */; };
2841022C2342D8E400EAFA29 /* Budget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2841022B2342D8E400EAFA29 /* Budget.swift */; };
@ -63,7 +64,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
2821265F23555FD300072D52 /* EditTransactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTransactionView.swift; sourceTree = "<group>"; };
2821265F23555FD300072D52 /* EditTransactionForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTransactionForm.swift; sourceTree = "<group>"; };
282126612357E45F00072D52 /* TransactionEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionEditView.swift; sourceTree = "<group>"; };
284102242341998300EAFA29 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
2841022623419A2B00EAFA29 /* TabbedBudgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbedBudgetView.swift; sourceTree = "<group>"; };
2841022B2342D8E400EAFA29 /* Budget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Budget.swift; sourceTree = "<group>"; };
@ -246,7 +248,8 @@
28FE6B03234449DC00D5543E /* TransactionListView.swift */,
28FE6B0523444A9800D5543E /* TransactionDetailsView.swift */,
28A1E959235006A300CA57FE /* AddTransactionView.swift */,
2821265F23555FD300072D52 /* EditTransactionView.swift */,
2821265F23555FD300072D52 /* EditTransactionForm.swift */,
282126612357E45F00072D52 /* TransactionEditView.swift */,
);
path = Transaction;
sourceTree = "<group>";
@ -406,7 +409,8 @@
28FE6AFE234428BF00D5543E /* DataStoreProvider.swift in Sources */,
28A1E95A235006A300CA57FE /* AddTransactionView.swift in Sources */,
28AC94EE233C373900BFB70A /* AppDelegate.swift in Sources */,
2821266023555FD300072D52 /* EditTransactionView.swift in Sources */,
2821266023555FD300072D52 /* EditTransactionForm.swift in Sources */,
282126622357E45F00072D52 /* TransactionEditView.swift in Sources */,
28AC94F0233C373900BFB70A /* SceneDelegate.swift in Sources */,
28FE6AFC23441E4500D5543E /* CategoryRepository.swift in Sources */,
2841022C2342D8E400EAFA29 /* Budget.swift in Sources */,

View file

@ -923,8 +923,8 @@
filePath = "BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "268"
endingLineNumber = "268"
startingLineNumber = "290"
endingLineNumber = "290"
landmarkName = "buildRequest(endPoint:method:data:)"
landmarkType = "7">
</BreakpointContent>
@ -939,87 +939,10 @@
filePath = "BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "267"
endingLineNumber = "267"
startingLineNumber = "289"
endingLineNumber = "289"
landmarkName = "buildRequest(endPoint:method:data:)"
landmarkType = "7">
<Locations>
<Location
uuid = "4657DB47-280A-41EE-B534-09C6F57D8DF8 - 662d42a959a71afc"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 (Foundation.Data, __C.NSURLResponse) throws -&gt; Foundation.Data in BudgetApp.RequestHelper.(buildRequest in _49C38939E0AD4FDF6F9F222ABD721B8E)&lt;A where A: Swift.Decodable, A: Swift.Encodable&gt;(endPoint: Swift.String, method: Swift.String, data: Swift.Optional&lt;Swift.Encodable&gt;) -&gt; Combine.AnyPublisher&lt;A, BudgetApp.NetworkError&gt;"
moduleName = "BudgetApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/billy/Projects/Budget/BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "267"
endingLineNumber = "267"
offsetFromSymbolStart = "540">
</Location>
<Location
uuid = "4657DB47-280A-41EE-B534-09C6F57D8DF8 - 662d42a959a71afc"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 (Foundation.Data, __C.NSURLResponse) throws -&gt; Foundation.Data in BudgetApp.RequestHelper.(buildRequest in _49C38939E0AD4FDF6F9F222ABD721B8E)&lt;A where A: Swift.Decodable, A: Swift.Encodable&gt;(endPoint: Swift.String, method: Swift.String, data: Swift.Optional&lt;Swift.Encodable&gt;) -&gt; Combine.AnyPublisher&lt;A, BudgetApp.NetworkError&gt;"
moduleName = "BudgetApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/billy/Projects/Budget/BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "267"
endingLineNumber = "267"
offsetFromSymbolStart = "760">
</Location>
<Location
uuid = "4657DB47-280A-41EE-B534-09C6F57D8DF8 - 662d42a959a71afc"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 (Foundation.Data, __C.NSURLResponse) throws -&gt; Foundation.Data in BudgetApp.RequestHelper.(buildRequest in _49C38939E0AD4FDF6F9F222ABD721B8E)&lt;A where A: Swift.Decodable, A: Swift.Encodable&gt;(endPoint: Swift.String, method: Swift.String, data: Swift.Optional&lt;Swift.Encodable&gt;) -&gt; Combine.AnyPublisher&lt;A, BudgetApp.NetworkError&gt;"
moduleName = "BudgetApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/billy/Projects/Budget/BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "267"
endingLineNumber = "267"
offsetFromSymbolStart = "928">
</Location>
<Location
uuid = "4657DB47-280A-41EE-B534-09C6F57D8DF8 - 662d42a959a71afc"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 (Foundation.Data, __C.NSURLResponse) throws -&gt; Foundation.Data in BudgetApp.RequestHelper.(buildRequest in _49C38939E0AD4FDF6F9F222ABD721B8E)&lt;A where A: Swift.Decodable, A: Swift.Encodable&gt;(endPoint: Swift.String, method: Swift.String, data: Swift.Optional&lt;Swift.Encodable&gt;) -&gt; Combine.AnyPublisher&lt;A, BudgetApp.NetworkError&gt;"
moduleName = "BudgetApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/billy/Projects/Budget/BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "267"
endingLineNumber = "267"
offsetFromSymbolStart = "1004">
</Location>
<Location
uuid = "4657DB47-280A-41EE-B534-09C6F57D8DF8 - 662d42a959a71afc"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 (Foundation.Data, __C.NSURLResponse) throws -&gt; Foundation.Data in BudgetApp.RequestHelper.(buildRequest in _49C38939E0AD4FDF6F9F222ABD721B8E)&lt;A where A: Swift.Decodable, A: Swift.Encodable&gt;(endPoint: Swift.String, method: Swift.String, data: Swift.Optional&lt;Swift.Encodable&gt;) -&gt; Combine.AnyPublisher&lt;A, BudgetApp.NetworkError&gt;"
moduleName = "BudgetApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/billy/Projects/Budget/BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "267"
endingLineNumber = "267"
offsetFromSymbolStart = "1176">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
@ -1032,8 +955,8 @@
filePath = "BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "271"
endingLineNumber = "271"
startingLineNumber = "293"
endingLineNumber = "293"
landmarkName = "buildRequest(endPoint:method:data:)"
landmarkType = "7">
</BreakpointContent>
@ -1054,22 +977,6 @@
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "81B540BD-DA72-4649-9624-E5B94E110061"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "278"
endingLineNumber = "278"
landmarkName = "buildRequest(endPoint:method:data:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
@ -1080,8 +987,8 @@
filePath = "BudgetApp/Network/BudgetApiService.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "274"
endingLineNumber = "274"
startingLineNumber = "296"
endingLineNumber = "296"
landmarkName = "buildRequest(endPoint:method:data:)"
landmarkType = "7">
</BreakpointContent>
@ -1128,8 +1035,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "131"
endingLineNumber = "131"
startingLineNumber = "150"
endingLineNumber = "150"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
@ -1144,8 +1051,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "132"
endingLineNumber = "132"
startingLineNumber = "151"
endingLineNumber = "151"
landmarkName = "stateContent"
landmarkType = "24">
<Locations>
@ -1192,8 +1099,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "109"
endingLineNumber = "109"
startingLineNumber = "128"
endingLineNumber = "128"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
@ -1208,8 +1115,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "110"
endingLineNumber = "110"
startingLineNumber = "129"
endingLineNumber = "129"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
@ -1224,8 +1131,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "85"
endingLineNumber = "85"
startingLineNumber = "104"
endingLineNumber = "104"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
@ -1240,8 +1147,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "86"
endingLineNumber = "86"
startingLineNumber = "105"
endingLineNumber = "105"
landmarkName = "stateContent"
landmarkType = "24">
<Locations>
@ -1288,8 +1195,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "87"
endingLineNumber = "87"
startingLineNumber = "106"
endingLineNumber = "106"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
@ -1304,8 +1211,8 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "111"
endingLineNumber = "111"
startingLineNumber = "130"
endingLineNumber = "130"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
@ -1320,11 +1227,187 @@
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "133"
endingLineNumber = "133"
startingLineNumber = "152"
endingLineNumber = "152"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "44E1F344-2A01-41C9-9CEF-3AA78B75786F"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "70"
endingLineNumber = "70"
landmarkName = "init(_:transactionId:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "F1B20B89-EC27-4275-B5C2-42731837346D"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionDetailsView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "22"
endingLineNumber = "22"
landmarkName = "body"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "CCDAD304-E9B9-415D-8C32-DD16F0145D35"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionEditView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "29"
endingLineNumber = "29"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "1443CC3F-35EB-4BFC-B8A0-AED0967623B3"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionEditView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "28"
endingLineNumber = "28"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "72CF2BC4-4793-45C9-AB03-7BF0435C967D"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionEditView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "30"
endingLineNumber = "30"
landmarkName = "stateContent"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "C4EFC6E6-28A6-4526-A39F-9ECBE3FD54C0"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionDataStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "104"
endingLineNumber = "104"
landmarkName = "deleteTransaction(_:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "10E6DF77-55D2-4A3B-9A2D-AB201E442563"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionDataStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "98"
endingLineNumber = "98"
landmarkName = "deleteTransaction(_:)"
landmarkType = "7">
<Locations>
<Location
uuid = "10E6DF77-55D2-4A3B-9A2D-AB201E442563 - 2056d968351e5ea4"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "BudgetApp.TransactionDataStore.__allocating_init(BudgetApp.TransactionRepository) -&gt; BudgetApp.TransactionDataStore"
moduleName = "BudgetApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/billy/Projects/Budget/BudgetApp/Transaction/TransactionDataStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "0"
endingLineNumber = "0"
offsetFromSymbolStart = "18">
</Location>
<Location
uuid = "10E6DF77-55D2-4A3B-9A2D-AB201E442563 - 2dd9a0446d059f36"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "BudgetApp.TransactionDataStore.init(BudgetApp.TransactionRepository) -&gt; BudgetApp.TransactionDataStore"
moduleName = "BudgetApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/billy/Projects/Budget/BudgetApp/Transaction/TransactionDataStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "13"
endingLineNumber = "13"
offsetFromSymbolStart = "103">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "196C979E-2C3E-490A-8146-7B292B6271E7"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionDataStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "101"
endingLineNumber = "101"
landmarkName = "deleteTransaction(_:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "9B227992-1D58-4610-82C8-0615315761C3"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BudgetApp/Transaction/TransactionDataStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "100"
endingLineNumber = "100"
landmarkName = "deleteTransaction(_:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View file

@ -240,8 +240,32 @@ class RequestHelper {
)
}
func delete<ResultType: Codable>(_ endPoint: String) -> AnyPublisher<ResultType, NetworkError> {
return buildRequest(endPoint: endPoint, method: "DELETE")
func delete(_ endPoint: String) -> AnyPublisher<Empty, NetworkError> {
// Delete requests return no body so they need a special request helper
guard let url = URL(string: self.baseUrl + endPoint) else {
return Result.Publisher(.failure(.invalidUrl)).eraseToAnyPublisher()
}
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "DELETE"
let task = URLSession.shared.dataTaskPublisher(for: request)
.tryMap { (_, res) -> Empty in
guard let response = res as? HTTPURLResponse, 200...299 ~= response.statusCode else {
switch (res as? HTTPURLResponse)?.statusCode {
case 400: throw NetworkError.badRequest
case 401, 403: throw NetworkError.unauthorized
case 404: throw NetworkError.notFound
default: throw NetworkError.unknown
}
}
return Empty()
}
.mapError {
return NetworkError.jsonParsingFailed($0)
}
return task.eraseToAnyPublisher()
}
private func buildRequest<ResultType: Codable>(
@ -251,9 +275,7 @@ class RequestHelper {
) -> AnyPublisher<ResultType, NetworkError> {
guard let url = URL(string: self.baseUrl + endPoint) else {
return Future<ResultType, NetworkError> { promise in
promise(.failure(.invalidUrl))
}.eraseToAnyPublisher()
return Result.Publisher(.failure(.invalidUrl)).eraseToAnyPublisher()
}
var request = URLRequest(url: url)
@ -308,6 +330,7 @@ enum NetworkError: Error, Equatable {
case loading
case unknown
case notFound
case deleted
case unauthorized
case badRequest
case invalidUrl

View file

@ -28,7 +28,7 @@ struct AddTransactionView: View {
case .failure(.loading):
return AnyView(EmbeddedLoadingView())
default:
return AnyView(EditTransactionView(
return AnyView(EditTransactionForm(
title: self.$title,
description: self.$description,
date: self.$date,
@ -36,7 +36,8 @@ struct AddTransactionView: View {
type: self.$type,
budgetId: self.$budgetId,
categoryId: self.$categoryId,
dataStoreProvider: self.dataStoreProvider
dataStoreProvider: self.dataStoreProvider,
deleteAction: nil
))
}
}
@ -63,6 +64,15 @@ struct AddTransactionView: View {
))
})
}
.onDisappear {
self.title = ""
self.description = ""
self.date = Date()
self.amount = ""
self.type = .expense
self.budgetId = nil
self.categoryId = nil
}
}
@ObservedObject var transactionDataStore: TransactionDataStore

View file

@ -8,7 +8,7 @@
import SwiftUI
struct EditTransactionView: View {
struct EditTransactionForm: View {
@Binding var title: String
@Binding var description: String
@Binding var date: Date
@ -16,7 +16,9 @@ struct EditTransactionView: View {
@Binding var type: TransactionType
@Binding var budgetId: Int?
@Binding var categoryId: Int?
@State private var showingAlert = false
let dataStoreProvider: DataStoreProvider
let deleteAction: (() -> ())?
var body: some View {
Form {
@ -32,6 +34,19 @@ struct EditTransactionView: View {
}
BudgetPicker(self.dataStoreProvider, budgetId: self.$budgetId)
CategoryPicker(self.dataStoreProvider, budgetId: self.$budgetId, categoryId: self.$categoryId)
if deleteAction != nil {
Button(action: {
self.showingAlert = true
}) {
Text("delete_transaction")
.foregroundColor(.red)
}
.alert(isPresented:$showingAlert) {
Alert(title: Text("confirm_delete_transaction"), message: nil, primaryButton: .destructive(Text("delete"), action: deleteAction), secondaryButton: .cancel())
}
} else {
EmptyView()
}
}
}
}

View file

@ -38,3 +38,17 @@ enum TransactionType: Int, CaseIterable, Identifiable, Hashable {
var id: TransactionType { self }
}
extension Transaction {
var type: TransactionType {
if (self.expense) {
return .expense
} else {
return .income
}
}
var amountString: String {
return String(Double(self.amount) / 100.0)
}
}

View file

@ -88,6 +88,28 @@ class TransactionDataStore: ObservableObject {
})
}
func deleteTransaction(_ transactionId: Int) {
self.transaction = .failure(.loading)
_ = self.transactionRepository.deleteTransaction(transactionId)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
return
case .failure(let error):
self.transaction = .failure(error)
}
}, receiveValue: { (empty) in
self.transaction = .failure(.deleted)
})
}
func reset() {
self.transaction = .failure(.unknown)
self.transactions = .failure(.loading)
}
let objectWillChange = ObservableObjectPublisher()
private let transactionRepository: TransactionRepository
init(_ transactionRepository: TransactionRepository) {

View file

@ -9,8 +9,17 @@
import SwiftUI
struct TransactionDetailsView: View {
@Environment(\.presentationMode) var presentationMode
@State var shouldNavigateUp: Bool = false
var body: some View {
stateContent
.onAppear {
if self.shouldNavigateUp {
self.presentationMode.wrappedValue.dismiss()
} else {
self.transactionDataStore.getTransaction(self.transactionId)
}
}
}
var stateContent: AnyView {
@ -35,23 +44,33 @@ struct TransactionDetailsView: View {
UserLineItem(self.dataStoreProvider, userId: transaction.createdBy)
}.padding()
}
.navigationBarItems(trailing: NavigationLink(destination: EmptyView()) {
.navigationBarItems(trailing: NavigationLink(
destination: TransactionEditView(
self.dataStoreProvider,
transaction: transaction,
shouldNavigateUp: self.$shouldNavigateUp
).navigationBarTitle("edit_transaction")
) {
Text("edit")
}))
case .failure(.loading):
return AnyView(EmbeddedLoadingView())
case.failure(.deleted):
self.presentationMode.wrappedValue.dismiss()
return AnyView(EmptyView())
default:
return AnyView(Text("transaction_details_error"))
}
}
let dataStoreProvider: DataStoreProvider
let transactionId: Int
@ObservedObject var transactionDataStore: TransactionDataStore
init(_ dataStoreProvider: DataStoreProvider, transactionId: Int) {
self.dataStoreProvider = dataStoreProvider
let transactionDataStore = dataStoreProvider.transactionDataStore()
transactionDataStore.getTransaction(transactionId)
self.transactionDataStore = transactionDataStore
self.transactionId = transactionId
}
}

View file

@ -0,0 +1,89 @@
//
// TransactionEditView.swift
// BudgetApp
//
// Created by Billy Brawner on 10/16/19.
// Copyright © 2019 William Brawner. All rights reserved.
//
import SwiftUI
struct TransactionEditView: View {
@Environment(\.presentationMode) var presentationMode
var title: State<String>
var description: State<String>
var date: State<Date>
var amount: State<String>
var type: State<TransactionType>
var budgetId: State<Int?>
var categoryId: State<Int?>
let createdBy: Int
let id: Int?
var shouldNavigateUp: Binding<Bool>
var stateContent: AnyView {
switch transactionDataStore.transaction {
case .success(_), .failure(.deleted):
self.shouldNavigateUp.wrappedValue = true
self.presentationMode.wrappedValue.dismiss()
return AnyView(EmptyView())
case .failure(.loading):
return AnyView(EmbeddedLoadingView())
default:
return AnyView(EditTransactionForm(
title: self.title.projectedValue,
description: self.description.projectedValue,
date: self.date.projectedValue,
amount: self.amount.projectedValue,
type: self.type.projectedValue,
budgetId: self.budgetId.projectedValue,
categoryId: self.categoryId.projectedValue,
dataStoreProvider: self.dataStoreProvider,
deleteAction: {
self.transactionDataStore.deleteTransaction(self.id!)
}
))
}
}
var body: some View {
stateContent
.navigationBarItems(trailing: Button("save") {
let amount = Double(self.amount.wrappedValue) ?? 0.0
self.transactionDataStore.saveTransaction(Transaction(
id: self.id,
title: self.title.wrappedValue,
description: self.description.wrappedValue,
date: self.date.wrappedValue,
amount: Int(amount * 100.0),
categoryId: self.categoryId.wrappedValue,
expense: self.type.wrappedValue == TransactionType.expense,
createdBy: self.createdBy,
budgetId: self.budgetId.wrappedValue!
))
})
}
@ObservedObject var transactionDataStore: TransactionDataStore
let dataStoreProvider: DataStoreProvider
init(_ dataStoreProvider: DataStoreProvider, transaction: Transaction, shouldNavigateUp: Binding<Bool>) {
self.dataStoreProvider = dataStoreProvider
self.transactionDataStore = dataStoreProvider.transactionDataStore()
self.createdBy = try! dataStoreProvider.authenticationDataStore().currentUser.get().id!
self.id = transaction.id
self.title = State<String>(initialValue: transaction.title)
self.description = State<String>(initialValue: transaction.description ?? "")
self.date = State<Date>(initialValue: transaction.date)
self.amount = State<String>(initialValue: transaction.amountString)
self.type = State<TransactionType>(initialValue: transaction.type)
self.budgetId = State<Int?>(initialValue: transaction.budgetId)
self.categoryId = State<Int?>(initialValue: transaction.categoryId)
self.shouldNavigateUp = shouldNavigateUp
}
}
struct TransactionEditView_Previews: PreviewProvider {
static var previews: some View {
TransactionEditView(MockDataStoreProvider(), transaction: MockTransactionRepository.transaction, shouldNavigateUp: .constant(false))
}
}

View file

@ -13,7 +13,9 @@ struct TransactionListView: View {
@ObservedObject var transactionDataStore: TransactionDataStore
var body: some View {
stateContent
stateContent.onAppear {
self.transactionDataStore.getTransactions(self.category)
}
}
var stateContent: AnyView {
@ -37,10 +39,11 @@ struct TransactionListView: View {
}
let dataStoreProvider: DataStoreProvider
let category: Category?
init(_ dataStoreProvider: DataStoreProvider, category: Category? = nil) {
self.dataStoreProvider = dataStoreProvider
self.transactionDataStore = dataStoreProvider.transactionDataStore()
self.transactionDataStore.getTransactions(category)
self.category = category
}
}

View file

@ -14,6 +14,7 @@ protocol TransactionRepository {
func getTransaction(_ transactionId: Int) -> AnyPublisher<Transaction, NetworkError>
func createTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError>
func updateTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError>
func deleteTransaction(_ transactionId: Int) -> AnyPublisher<Empty, NetworkError>
}
class NetworkTransactionRepository: TransactionRepository {
@ -38,6 +39,10 @@ class NetworkTransactionRepository: TransactionRepository {
func updateTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError> {
return apiService.updateTransaction(transaction)
}
func deleteTransaction(_ transactionId: Int) -> AnyPublisher<Empty, NetworkError> {
return apiService.deleteTransaction(transactionId)
}
}
#if DEBUG
@ -69,5 +74,9 @@ class MockTransactionRepository: TransactionRepository {
func updateTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError> {
return Result.Publisher(MockTransactionRepository.transaction).eraseToAnyPublisher()
}
func deleteTransaction(_ transactionId: Int) -> AnyPublisher<Empty, NetworkError> {
return Result.Publisher(.success(Empty())).eraseToAnyPublisher()
}
}
#endif