Get live previews to work
Signed-off-by: Billy Brawner <billy@wbrawner.com>
This commit is contained in:
parent
fb7aa8fec4
commit
5d76390e67
50 changed files with 639 additions and 253 deletions
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Budget.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// BudgetRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 9/30/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class BudgetRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getBudgets() -> AnyPublisher<[Budget], NetworkError> {
|
||||
return apiService.getBudgets()
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// CategoryRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 10/1/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class CategoryRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getCategories(budgetId: Int? = nil, count: Int? = nil, page: Int? = nil) -> AnyPublisher<[Category], NetworkError> {
|
||||
return apiService.getCategories(budgetId: budgetId, count: count, page: page)
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// TransactionDetailsView.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 10/1/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TransactionDetailsView: View {
|
||||
@State var title: String = ""
|
||||
@State var description: String? = nil
|
||||
@State var date: Date = Date()
|
||||
@State var amount: Int = 0
|
||||
@State var category: Category? = nil
|
||||
@State var expense: Bool = true
|
||||
@State var createdBy: User? = nil
|
||||
@State var budget: Budget? = nil
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
TextField("prompt_title", text: self.$title)
|
||||
// DatePicker(
|
||||
}
|
||||
}
|
||||
|
||||
init(_ dataStoreProvider: DataStoreProvider, transaction: Transaction) {
|
||||
// self.title = transaction.title
|
||||
// self.description = transaction.description
|
||||
// self.date = transaction.date
|
||||
// self.amount = transaction.amount
|
||||
// self.category = transaction.category
|
||||
// self.expense = transaction.expense
|
||||
// self.createdBy = transaction.createdBy
|
||||
// self.budget = transaction.budget
|
||||
}
|
||||
}
|
||||
|
||||
//struct TransactionDetailsView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// TransactionDetailsView()
|
||||
// }
|
||||
//}
|
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// TransactionRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 9/25/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class TransactionRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getTransactions(categoryIds: [Int]? = nil, from: Date? = nil, count: Int? = nil, page: Int? = nil) -> AnyPublisher<[Transaction], NetworkError> {
|
||||
return apiService.getTransactions(categoryIds: categoryIds, from: from, count: count, page: page)
|
||||
}
|
||||
|
||||
func createTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError> {
|
||||
return apiService.newTransaction(transaction)
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// UserRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 9/25/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class UserRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getUser(id: Int) -> AnyPublisher<User, NetworkError> {
|
||||
return apiService.getUser(id: id)
|
||||
}
|
||||
|
||||
func searchUsers(withUsername: String) -> AnyPublisher<[User], NetworkError> {
|
||||
return apiService.searchUsers(query: withUsername)
|
||||
}
|
||||
|
||||
func login(username: String, password: String) -> AnyPublisher<User, NetworkError> {
|
||||
return apiService.login(username: username, password: password)
|
||||
}
|
||||
|
||||
func register(username: String, email: String, password: String) -> AnyPublisher<User, NetworkError> {
|
||||
return apiService.register(username: username, email: email, password: password)
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@
|
|||
2857EAEC233DA30B0026BC83 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
|
||||
2888234623512DBF003D3847 /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = "<group>"; };
|
||||
28A1E959235006A300CA57FE /* AddTransactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTransactionView.swift; sourceTree = "<group>"; };
|
||||
28AC94EA233C373900BFB70A /* Budget.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Budget.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28AC94EA233C373900BFB70A /* BudgetApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BudgetApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28AC94ED233C373900BFB70A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
28AC94EF233C373900BFB70A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
28AC94F1233C373900BFB70A /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
||||
|
@ -76,10 +76,10 @@
|
|||
28AC94F6233C373A00BFB70A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
28AC94F9233C373A00BFB70A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
28AC94FB233C373A00BFB70A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
28AC9500233C373A00BFB70A /* BudgetTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BudgetTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28AC9500233C373A00BFB70A /* BudgetAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BudgetAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28AC9504233C373A00BFB70A /* BudgetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BudgetTests.swift; sourceTree = "<group>"; };
|
||||
28AC9506233C373A00BFB70A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
28AC950B233C373A00BFB70A /* BudgetUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BudgetUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28AC950B233C373A00BFB70A /* BudgetAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BudgetAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28AC950F233C373A00BFB70A /* BudgetUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BudgetUITests.swift; sourceTree = "<group>"; };
|
||||
28AC9511233C373A00BFB70A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
28AC9520233C381C00BFB70A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
|
@ -161,9 +161,9 @@
|
|||
28AC94E1233C373900BFB70A = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28AC94EC233C373900BFB70A /* Budget */,
|
||||
28AC9503233C373A00BFB70A /* BudgetTests */,
|
||||
28AC950E233C373A00BFB70A /* BudgetUITests */,
|
||||
28AC94EC233C373900BFB70A /* BudgetApp */,
|
||||
28AC9503233C373A00BFB70A /* BudgetAppTests */,
|
||||
28AC950E233C373A00BFB70A /* BudgetAppUITests */,
|
||||
28AC94EB233C373900BFB70A /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
|
@ -171,14 +171,14 @@
|
|||
28AC94EB233C373900BFB70A /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28AC94EA233C373900BFB70A /* Budget.app */,
|
||||
28AC9500233C373A00BFB70A /* BudgetTests.xctest */,
|
||||
28AC950B233C373A00BFB70A /* BudgetUITests.xctest */,
|
||||
28AC94EA233C373900BFB70A /* BudgetApp.app */,
|
||||
28AC9500233C373A00BFB70A /* BudgetAppTests.xctest */,
|
||||
28AC950B233C373A00BFB70A /* BudgetAppUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28AC94EC233C373900BFB70A /* Budget */ = {
|
||||
28AC94EC233C373900BFB70A /* BudgetApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2841022A2342D8CB00EAFA29 /* Category */,
|
||||
|
@ -201,7 +201,7 @@
|
|||
28FE6AFD234428BF00D5543E /* DataStoreProvider.swift */,
|
||||
2888234623512DBF003D3847 /* Observable.swift */,
|
||||
);
|
||||
path = Budget;
|
||||
path = BudgetApp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28AC94F5233C373A00BFB70A /* Preview Content */ = {
|
||||
|
@ -212,22 +212,22 @@
|
|||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28AC9503233C373A00BFB70A /* BudgetTests */ = {
|
||||
28AC9503233C373A00BFB70A /* BudgetAppTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28AC9504233C373A00BFB70A /* BudgetTests.swift */,
|
||||
28AC9506233C373A00BFB70A /* Info.plist */,
|
||||
);
|
||||
path = BudgetTests;
|
||||
path = BudgetAppTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28AC950E233C373A00BFB70A /* BudgetUITests */ = {
|
||||
28AC950E233C373A00BFB70A /* BudgetAppUITests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28AC950F233C373A00BFB70A /* BudgetUITests.swift */,
|
||||
28AC9511233C373A00BFB70A /* Info.plist */,
|
||||
);
|
||||
path = BudgetUITests;
|
||||
path = BudgetAppUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28AC9526233C42F800BFB70A /* Transaction */ = {
|
||||
|
@ -264,9 +264,9 @@
|
|||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
28AC94E9233C373900BFB70A /* Budget */ = {
|
||||
28AC94E9233C373900BFB70A /* BudgetApp */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 28AC9514233C373A00BFB70A /* Build configuration list for PBXNativeTarget "Budget" */;
|
||||
buildConfigurationList = 28AC9514233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetApp" */;
|
||||
buildPhases = (
|
||||
28AC94E6233C373900BFB70A /* Sources */,
|
||||
28AC94E7233C373900BFB70A /* Frameworks */,
|
||||
|
@ -276,14 +276,14 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Budget;
|
||||
name = BudgetApp;
|
||||
productName = Budget;
|
||||
productReference = 28AC94EA233C373900BFB70A /* Budget.app */;
|
||||
productReference = 28AC94EA233C373900BFB70A /* BudgetApp.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
28AC94FF233C373A00BFB70A /* BudgetTests */ = {
|
||||
28AC94FF233C373A00BFB70A /* BudgetAppTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 28AC9517233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetTests" */;
|
||||
buildConfigurationList = 28AC9517233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetAppTests" */;
|
||||
buildPhases = (
|
||||
28AC94FC233C373A00BFB70A /* Sources */,
|
||||
28AC94FD233C373A00BFB70A /* Frameworks */,
|
||||
|
@ -294,14 +294,14 @@
|
|||
dependencies = (
|
||||
28AC9502233C373A00BFB70A /* PBXTargetDependency */,
|
||||
);
|
||||
name = BudgetTests;
|
||||
name = BudgetAppTests;
|
||||
productName = BudgetTests;
|
||||
productReference = 28AC9500233C373A00BFB70A /* BudgetTests.xctest */;
|
||||
productReference = 28AC9500233C373A00BFB70A /* BudgetAppTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
28AC950A233C373A00BFB70A /* BudgetUITests */ = {
|
||||
28AC950A233C373A00BFB70A /* BudgetAppUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 28AC951A233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetUITests" */;
|
||||
buildConfigurationList = 28AC951A233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetAppUITests" */;
|
||||
buildPhases = (
|
||||
28AC9507233C373A00BFB70A /* Sources */,
|
||||
28AC9508233C373A00BFB70A /* Frameworks */,
|
||||
|
@ -312,9 +312,9 @@
|
|||
dependencies = (
|
||||
28AC950D233C373A00BFB70A /* PBXTargetDependency */,
|
||||
);
|
||||
name = BudgetUITests;
|
||||
name = BudgetAppUITests;
|
||||
productName = BudgetUITests;
|
||||
productReference = 28AC950B233C373A00BFB70A /* BudgetUITests.xctest */;
|
||||
productReference = 28AC950B233C373A00BFB70A /* BudgetAppUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
@ -340,7 +340,7 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 28AC94E5233C373900BFB70A /* Build configuration list for PBXProject "Budget" */;
|
||||
buildConfigurationList = 28AC94E5233C373900BFB70A /* Build configuration list for PBXProject "BudgetApp" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
|
@ -354,9 +354,9 @@
|
|||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
28AC94E9233C373900BFB70A /* Budget */,
|
||||
28AC94FF233C373A00BFB70A /* BudgetTests */,
|
||||
28AC950A233C373A00BFB70A /* BudgetUITests */,
|
||||
28AC94E9233C373900BFB70A /* BudgetApp */,
|
||||
28AC94FF233C373A00BFB70A /* BudgetAppTests */,
|
||||
28AC950A233C373A00BFB70A /* BudgetAppUITests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -445,12 +445,12 @@
|
|||
/* Begin PBXTargetDependency section */
|
||||
28AC9502233C373A00BFB70A /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 28AC94E9233C373900BFB70A /* Budget */;
|
||||
target = 28AC94E9233C373900BFB70A /* BudgetApp */;
|
||||
targetProxy = 28AC9501233C373A00BFB70A /* PBXContainerItemProxy */;
|
||||
};
|
||||
28AC950D233C373A00BFB70A /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 28AC94E9233C373900BFB70A /* Budget */;
|
||||
target = 28AC94E9233C373900BFB70A /* BudgetApp */;
|
||||
targetProxy = 28AC950C233C373A00BFB70A /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
@ -532,6 +532,7 @@
|
|||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
|
@ -586,6 +587,7 @@
|
|||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_NAME = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
|
@ -598,10 +600,10 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Budget/Preview Content\"";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"BudgetApp/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = VJ33S6H7W7;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = Budget/Info.plist;
|
||||
INFOPLIST_FILE = BudgetApp/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -618,10 +620,10 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Budget/Preview Content\"";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"BudgetApp/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = VJ33S6H7W7;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = Budget/Info.plist;
|
||||
INFOPLIST_FILE = BudgetApp/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -639,7 +641,7 @@
|
|||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = BudgetTests/Info.plist;
|
||||
INFOPLIST_FILE = BudgetAppTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -650,7 +652,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Budget.app/Budget";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BudgetApp.app/BudgetApp";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -660,7 +662,7 @@
|
|||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = BudgetTests/Info.plist;
|
||||
INFOPLIST_FILE = BudgetAppTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -671,7 +673,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Budget.app/Budget";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BudgetApp.app/BudgetApp";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
@ -680,7 +682,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = BudgetUITests/Info.plist;
|
||||
INFOPLIST_FILE = BudgetAppUITests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -699,7 +701,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = BudgetUITests/Info.plist;
|
||||
INFOPLIST_FILE = BudgetAppUITests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -716,7 +718,7 @@
|
|||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
28AC94E5233C373900BFB70A /* Build configuration list for PBXProject "Budget" */ = {
|
||||
28AC94E5233C373900BFB70A /* Build configuration list for PBXProject "BudgetApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
28AC9512233C373A00BFB70A /* Debug */,
|
||||
|
@ -725,7 +727,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
28AC9514233C373A00BFB70A /* Build configuration list for PBXNativeTarget "Budget" */ = {
|
||||
28AC9514233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
28AC9515233C373A00BFB70A /* Debug */,
|
||||
|
@ -734,7 +736,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
28AC9517233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetTests" */ = {
|
||||
28AC9517233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetAppTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
28AC9518233C373A00BFB70A /* Debug */,
|
||||
|
@ -743,7 +745,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
28AC951A233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetUITests" */ = {
|
||||
28AC951A233C373A00BFB70A /* Build configuration list for PBXNativeTarget "BudgetAppUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
28AC951B233C373A00BFB70A /* Debug */,
|
|
@ -2,6 +2,6 @@
|
|||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Budget.xcodeproj">
|
||||
location = "self:/Users/billy/Projects/Budget/BudgetApp.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,98 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1100"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "28AC94E9233C373900BFB70A"
|
||||
BuildableName = "BudgetApp.app"
|
||||
BlueprintName = "BudgetApp"
|
||||
ReferencedContainer = "container:BudgetApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "28AC94FF233C373A00BFB70A"
|
||||
BuildableName = "BudgetAppTests.xctest"
|
||||
BlueprintName = "BudgetAppTests"
|
||||
ReferencedContainer = "container:BudgetApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "28AC950A233C373A00BFB70A"
|
||||
BuildableName = "BudgetAppUITests.xctest"
|
||||
BlueprintName = "BudgetAppUITests"
|
||||
ReferencedContainer = "container:BudgetApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "28AC94E9233C373900BFB70A"
|
||||
BuildableName = "BudgetApp.app"
|
||||
BlueprintName = "BudgetApp"
|
||||
ReferencedContainer = "container:BudgetApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "28AC94E9233C373900BFB70A"
|
||||
BuildableName = "BudgetApp.app"
|
||||
BlueprintName = "BudgetApp"
|
||||
ReferencedContainer = "container:BudgetApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -701,7 +701,7 @@
|
|||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "52"
|
||||
endingLineNumber = "52"
|
||||
landmarkName = "createTransaction(_:)"
|
||||
landmarkName = "getTransaction(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
|
@ -717,7 +717,7 @@
|
|||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "59"
|
||||
endingLineNumber = "59"
|
||||
landmarkName = "createTransaction(_:)"
|
||||
landmarkName = "getTransaction(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
|
@ -733,7 +733,7 @@
|
|||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "47"
|
||||
endingLineNumber = "47"
|
||||
landmarkName = "createTransaction(_:)"
|
||||
landmarkName = "getTransactions(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
|
@ -813,8 +813,8 @@
|
|||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "24"
|
||||
endingLineNumber = "24"
|
||||
landmarkName = "createTransaction(_:)"
|
||||
landmarkType = "7">
|
||||
landmarkName = "NetworkTransactionRepository"
|
||||
landmarkType = "3">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
|
@ -849,5 +849,37 @@
|
|||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "E7F93AB9-4460-441B-9C79-F4B83BB30027"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Budget/Budget/BudgetRepository.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "26"
|
||||
endingLineNumber = "26"
|
||||
landmarkName = "init(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "E04190C6-277F-4BEF-B485-DD319C1514ED"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Budget/Budget/BudgetRepository.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "27"
|
||||
endingLineNumber = "27"
|
||||
landmarkName = "NetworkBudgetRepository"
|
||||
landmarkType = "3">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>BudgetApp.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>28AC94E9233C373900BFB70A</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>28AC94FF233C373A00BFB70A</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>28AC950A233C373A00BFB70A</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
//
|
||||
// BudgetsView.swift
|
||||
// Budget
|
||||
|
@ -74,8 +75,8 @@ struct BudgetListItemView: View {
|
|||
}
|
||||
}
|
||||
|
||||
//struct BudgetsView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// BudgetsView(budgets: [])
|
||||
// }
|
||||
//}
|
||||
struct BudgetListsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
BudgetListsView(MockDataStoreProvider())
|
||||
}
|
||||
}
|
101
BudgetApp/Budget/BudgetRepository.swift
Normal file
101
BudgetApp/Budget/BudgetRepository.swift
Normal file
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// BudgetRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 9/30/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
protocol BudgetRepository {
|
||||
func getBudgets(count: Int?, page: Int?) -> AnyPublisher<[Budget], NetworkError>
|
||||
func getBudget(_ id: Int) -> AnyPublisher<Budget, NetworkError>
|
||||
func getBudgetBalance(_ id: Int) -> AnyPublisher<Int, NetworkError>
|
||||
func newBudget(_ budget: Budget) -> AnyPublisher<Budget, NetworkError>
|
||||
func updateBudget(_ budget: Budget) -> AnyPublisher<Budget, NetworkError>
|
||||
func deleteBudget(_ id: Int) -> AnyPublisher<Empty, NetworkError>
|
||||
}
|
||||
|
||||
class NetworkBudgetRepository: BudgetRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getBudgets(count: Int?, page: Int?) -> AnyPublisher<[Budget], NetworkError> {
|
||||
return apiService.getBudgets(count: count, page: page)
|
||||
}
|
||||
|
||||
func getBudget(_ id: Int) -> AnyPublisher<Budget, NetworkError> {
|
||||
return apiService.getBudget(id)
|
||||
}
|
||||
|
||||
func getBudgetBalance(_ id: Int) -> AnyPublisher<Int, NetworkError> {
|
||||
return apiService.getBudgetBalance(id)
|
||||
}
|
||||
|
||||
func newBudget(_ budget: Budget) -> AnyPublisher<Budget, NetworkError> {
|
||||
return apiService.newBudget(budget)
|
||||
}
|
||||
|
||||
func updateBudget(_ budget: Budget) -> AnyPublisher<Budget, NetworkError> {
|
||||
return apiService.updateBudget(budget)
|
||||
}
|
||||
|
||||
func deleteBudget(_ id: Int) -> AnyPublisher<Empty, NetworkError> {
|
||||
return apiService.deleteBudget(id)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
class MockBudgetRepository: BudgetRepository {
|
||||
func getBudgets(count: Int?, page: Int?) -> AnyPublisher<[Budget], NetworkError> {
|
||||
return Result.Publisher([Budget(
|
||||
id: 1,
|
||||
name: "Test Budget",
|
||||
description: "A mock budget used for testing",
|
||||
users: []
|
||||
)]).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func getBudget(_ id: Int) -> AnyPublisher<Budget, NetworkError> {
|
||||
return Result.Publisher(Budget(
|
||||
id: 1,
|
||||
name: "Test Budget",
|
||||
description: "A mock budget used for testing",
|
||||
users: []
|
||||
)).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func getBudgetBalance(_ id: Int) -> AnyPublisher<Int, NetworkError> {
|
||||
return Result.Publisher(10000).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func newBudget(_ budget: Budget) -> AnyPublisher<Budget, NetworkError> {
|
||||
return Result.Publisher(Budget(
|
||||
id: 1,
|
||||
name: "Test Budget",
|
||||
description: "A mock budget used for testing",
|
||||
users: []
|
||||
)).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func updateBudget(_ budget: Budget) -> AnyPublisher<Budget, NetworkError> {
|
||||
return Result.Publisher(Budget(
|
||||
id: 1,
|
||||
name: "Test Budget",
|
||||
description: "A mock budget used for testing",
|
||||
users: []
|
||||
)).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func deleteBudget(_ id: Int) -> AnyPublisher<Empty, NetworkError> {
|
||||
return Result.Publisher(Empty()).eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -21,10 +21,10 @@ class BudgetsDataStore: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func getBudgets() {
|
||||
func getBudgets(count: Int? = nil, page: Int? = nil) {
|
||||
self.budgets = .failure(.loading)
|
||||
|
||||
_ = self.budgetRepository.getBudgets()
|
||||
_ = self.budgetRepository.getBudgets(count: count, page: page)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(receiveCompletion: { (status) in
|
||||
switch status {
|
36
BudgetApp/Category/CategoryRepository.swift
Normal file
36
BudgetApp/Category/CategoryRepository.swift
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// CategoryRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 10/1/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
protocol CategoryRepository {
|
||||
func getCategories(budgetId: Int?, count: Int?, page: Int?) -> AnyPublisher<[Category], NetworkError>
|
||||
}
|
||||
|
||||
class NetworkCategoryRepository: CategoryRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getCategories(budgetId: Int?, count: Int?, page: Int?) -> AnyPublisher<[Category], NetworkError> {
|
||||
return apiService.getCategories(budgetId: budgetId, count: count, page: page)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
class MockCategoryRepository: CategoryRepository {
|
||||
func getCategories(budgetId: Int?, count: Int?, page: Int?) -> AnyPublisher<[Category], NetworkError> {
|
||||
return Result.Publisher([]).eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -15,7 +15,6 @@ class DataStoreProvider {
|
|||
private let budgetRepository: BudgetRepository
|
||||
private let categoryRepository: CategoryRepository
|
||||
private let transactionRepository: TransactionRepository
|
||||
private let userRepository: UserRepository
|
||||
|
||||
private let _userDataStore: UserDataStore
|
||||
|
||||
|
@ -35,14 +34,30 @@ class DataStoreProvider {
|
|||
return self._userDataStore
|
||||
}
|
||||
|
||||
init(_ baseUrl: String) {
|
||||
let requestHelper = RequestHelper(baseUrl)
|
||||
let apiService = BudgetApiService(requestHelper)
|
||||
budgetRepository = BudgetRepository(apiService)
|
||||
categoryRepository = CategoryRepository(apiService)
|
||||
transactionRepository = TransactionRepository(apiService)
|
||||
userRepository = UserRepository(apiService)
|
||||
|
||||
_userDataStore = UserDataStore(userRepository)
|
||||
init(
|
||||
budgetRepository: BudgetRepository,
|
||||
categoryRepository: CategoryRepository,
|
||||
transactionRepository: TransactionRepository,
|
||||
userRepository: UserRepository
|
||||
) {
|
||||
self.budgetRepository = budgetRepository
|
||||
self.categoryRepository = categoryRepository
|
||||
self.transactionRepository = transactionRepository
|
||||
self._userDataStore = UserDataStore(userRepository)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
class MockDataStoreProvider: DataStoreProvider {
|
||||
init() {
|
||||
super.init(
|
||||
budgetRepository: MockBudgetRepository(),
|
||||
categoryRepository: MockCategoryRepository(),
|
||||
transactionRepository: MockTransactionRepository(),
|
||||
userRepository: MockUserRepository()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -45,10 +45,9 @@ class BudgetApiService {
|
|||
return requestHelper.put("/budgets/\(budget.id!)", data: budget)
|
||||
}
|
||||
|
||||
// TODO: Figure out how to implement this
|
||||
// func deleteBudget(_ id: Int) -> AnyPublisher<Void, NetworkError> {
|
||||
// return requestHelper.delete("/budgets/\(id)")
|
||||
// }
|
||||
func deleteBudget(_ id: Int) -> AnyPublisher<Empty, NetworkError> {
|
||||
return requestHelper.delete("/budgets/\(id)")
|
||||
}
|
||||
|
||||
// MARK: Transactions
|
||||
|
||||
|
@ -94,10 +93,9 @@ class BudgetApiService {
|
|||
return requestHelper.put("/transactions/\(transaction.id!)", data: transaction)
|
||||
}
|
||||
|
||||
// TODO: Figure out how to implement this
|
||||
// func deleteTransaction(_ id: Int) -> AnyPublisher<Void, NetworkError> {
|
||||
// return requestHelper.delete("/transactions/\(id)")
|
||||
// }
|
||||
func deleteTransaction(_ id: Int) -> AnyPublisher<Empty, NetworkError> {
|
||||
return requestHelper.delete("/transactions/\(id)")
|
||||
}
|
||||
|
||||
// MARK: Categories
|
||||
|
||||
|
@ -131,10 +129,9 @@ class BudgetApiService {
|
|||
return requestHelper.put("/categories/\(category.id!)", data: category)
|
||||
}
|
||||
|
||||
// TODO: Figure out how to implement this
|
||||
// func deleteCategory(_ id: Int) -> AnyPublisher<Void, NetworkError> {
|
||||
// return requestHelper.delete("/categories/\(id)")
|
||||
// }
|
||||
func deleteCategory(_ id: Int) -> AnyPublisher<Empty, NetworkError> {
|
||||
return requestHelper.delete("/categories/\(id)")
|
||||
}
|
||||
|
||||
// MARK: Users
|
||||
func login(username: String, password: String) -> AnyPublisher<User, NetworkError> {
|
||||
|
@ -188,6 +185,10 @@ class BudgetApiService {
|
|||
func updateUser(_ user: User) -> AnyPublisher<User, NetworkError> {
|
||||
return requestHelper.put("/users/\(user.id!)", data: user)
|
||||
}
|
||||
|
||||
func deleteUser(_ user: User) -> AnyPublisher<Empty, NetworkError> {
|
||||
return requestHelper.delete("/users/\(user.id!)")
|
||||
}
|
||||
}
|
||||
|
||||
class RequestHelper {
|
||||
|
@ -280,6 +281,8 @@ class RequestHelper {
|
|||
}
|
||||
}
|
||||
|
||||
struct Empty: Codable {}
|
||||
|
||||
enum NetworkError: Error, Equatable {
|
||||
static func == (lhs: NetworkError, rhs: NetworkError) -> Bool {
|
||||
switch (lhs, rhs) {
|
|
@ -22,7 +22,19 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
#else
|
||||
let baseUrl = "https://budget-api.intra.wbrawner.com"
|
||||
#endif
|
||||
dataStoreProvider = DataStoreProvider(baseUrl)
|
||||
let requestHelper = RequestHelper(baseUrl)
|
||||
let apiService = BudgetApiService(requestHelper)
|
||||
let budgetRepository = MockBudgetRepository()
|
||||
// let budgetRepository = NetworkBudgetRepository(apiService)
|
||||
let categoryRepository = NetworkCategoryRepository(apiService)
|
||||
let transactionRepository = NetworkTransactionRepository(apiService)
|
||||
let userRepository = NetworkUserRepository(apiService)
|
||||
dataStoreProvider = DataStoreProvider(
|
||||
budgetRepository: budgetRepository,
|
||||
categoryRepository: categoryRepository,
|
||||
transactionRepository: transactionRepository,
|
||||
userRepository: userRepository
|
||||
)
|
||||
super.init()
|
||||
}
|
||||
|
|
@ -29,9 +29,7 @@ struct AddTransactionView: View {
|
|||
return AnyView(EmptyView())
|
||||
|
||||
case .failure(.loading):
|
||||
return AnyView(VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
})
|
||||
return AnyView(EmbeddedLoadingView())
|
||||
default:
|
||||
// TODO: Handle each network failure type
|
||||
return AnyView(Form {
|
||||
|
@ -66,9 +64,7 @@ struct AddTransactionView: View {
|
|||
})
|
||||
)
|
||||
default:
|
||||
return AnyView(VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
})
|
||||
return AnyView(EmbeddedLoadingView())
|
||||
}
|
||||
}
|
||||
|
|
@ -22,14 +22,19 @@ class TransactionDataStore: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func getTransactions(_ category: Category? = nil) {
|
||||
func getTransactions(_ category: Category? = nil, from: Date? = nil, count: Int? = nil, page: Int? = nil) {
|
||||
self.transactions = .failure(.loading)
|
||||
|
||||
var categoryIds: [Int] = []
|
||||
if category != nil {
|
||||
categoryIds.append(category!.id!)
|
||||
}
|
||||
_ = self.transactionRepository.getTransactions(categoryIds: categoryIds, from: Date(timeIntervalSince1970: 0))
|
||||
_ = self.transactionRepository.getTransactions(
|
||||
categoryIds: categoryIds,
|
||||
from: Date(timeIntervalSince1970: 0),
|
||||
count: count,
|
||||
page: page
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(receiveCompletion: { (completion) in
|
||||
switch completion {
|
||||
|
@ -43,6 +48,23 @@ class TransactionDataStore: ObservableObject {
|
|||
})
|
||||
}
|
||||
|
||||
func getTransaction(_ transactionId: Int) {
|
||||
self.transaction = .failure(.loading)
|
||||
|
||||
_ = self.transactionRepository.getTransaction(transactionId)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(receiveCompletion: { (completion) in
|
||||
switch completion {
|
||||
case .finished:
|
||||
return
|
||||
case .failure(let error):
|
||||
self.transaction = .failure(error)
|
||||
}
|
||||
}, receiveValue: { (transaction) in
|
||||
self.transaction = .success(transaction)
|
||||
})
|
||||
}
|
||||
|
||||
func createTransaction(_ transaction: Transaction) {
|
||||
self.transaction = .failure(.loading)
|
||||
|
41
BudgetApp/Transaction/TransactionDetailsView.swift
Normal file
41
BudgetApp/Transaction/TransactionDetailsView.swift
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// TransactionDetailsView.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 10/1/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TransactionDetailsView: View {
|
||||
var body: some View {
|
||||
stateContent
|
||||
}
|
||||
|
||||
var stateContent: AnyView {
|
||||
switch transactionDataStore.transaction {
|
||||
case .success(let transaction):
|
||||
return AnyView(VStack {
|
||||
Text(transaction.title)
|
||||
})
|
||||
case .failure(.loading):
|
||||
return AnyView(EmbeddedLoadingView())
|
||||
default:
|
||||
return AnyView(Text("transaction_details_error"))
|
||||
}
|
||||
}
|
||||
|
||||
let transactionDataStore: TransactionDataStore
|
||||
init(_ dataStoreProvider: DataStoreProvider, transaction: Transaction) {
|
||||
let transactionDataStore = dataStoreProvider.transactionDataStore()
|
||||
transactionDataStore.getTransactions()
|
||||
self.transactionDataStore = transactionDataStore
|
||||
}
|
||||
}
|
||||
|
||||
//struct TransactionDetailsView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// TransactionDetailsView()
|
||||
// }
|
||||
//}
|
82
BudgetApp/Transaction/TransactionRepository.swift
Normal file
82
BudgetApp/Transaction/TransactionRepository.swift
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// TransactionRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 9/25/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
protocol TransactionRepository {
|
||||
func getTransactions(categoryIds: [Int]?, from: Date?, count: Int?, page: Int?) -> AnyPublisher<[Transaction], NetworkError>
|
||||
func getTransaction(_ transactionId: Int) -> AnyPublisher<Transaction, NetworkError>
|
||||
func createTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError>
|
||||
}
|
||||
|
||||
class NetworkTransactionRepository: TransactionRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getTransactions(categoryIds: [Int]?, from: Date?, count: Int?, page: Int?) -> AnyPublisher<[Transaction], NetworkError> {
|
||||
return apiService.getTransactions(categoryIds: categoryIds, from: from, count: count, page: page)
|
||||
}
|
||||
|
||||
func getTransaction(_ transactionId: Int) -> AnyPublisher<Transaction, NetworkError> {
|
||||
return apiService.getTransaction(transactionId)
|
||||
}
|
||||
|
||||
func createTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError> {
|
||||
return apiService.newTransaction(transaction)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
class MockTransactionRepository: TransactionRepository {
|
||||
func getTransactions(categoryIds: [Int]? = nil, from: Date? = nil, count: Int? = nil, page: Int? = nil) -> AnyPublisher<[Transaction], NetworkError> {
|
||||
return Result.Publisher([Transaction(
|
||||
id: 2,
|
||||
title: "Test Transaction",
|
||||
description: "A mock transaction used for testing",
|
||||
date: Date(),
|
||||
amount: 10000,
|
||||
categoryId: 3,
|
||||
expense: true,
|
||||
createdBy: 0,
|
||||
budgetId: 1
|
||||
)]).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func getTransaction(_ transactionId: Int) -> AnyPublisher<Transaction, NetworkError> {
|
||||
return Result.Publisher(Transaction(
|
||||
id: 2,
|
||||
title: "Test Transaction",
|
||||
description: "A mock transaction used for testing",
|
||||
date: Date(),
|
||||
amount: 10000,
|
||||
categoryId: 3,
|
||||
expense: true,
|
||||
createdBy: 0,
|
||||
budgetId: 1
|
||||
)).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func createTransaction(_ transaction: Transaction) -> AnyPublisher<Transaction, NetworkError> {
|
||||
return Result.Publisher(Transaction(
|
||||
id: 2,
|
||||
title: "Test Transaction",
|
||||
description: "A mock transaction used for testing",
|
||||
date: Date(),
|
||||
amount: 10000,
|
||||
categoryId: 3,
|
||||
expense: true,
|
||||
createdBy: 0,
|
||||
budgetId: 1
|
||||
)).eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
#endif
|
67
BudgetApp/User/UserRepository.swift
Normal file
67
BudgetApp/User/UserRepository.swift
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// UserRepository.swift
|
||||
// Budget
|
||||
//
|
||||
// Created by Billy Brawner on 9/25/19.
|
||||
// Copyright © 2019 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
protocol UserRepository {
|
||||
func getUser(id: Int) -> AnyPublisher<User, NetworkError>
|
||||
func searchUsers(withUsername: String) -> AnyPublisher<[User], NetworkError>
|
||||
func login(username: String, password: String) -> AnyPublisher<User, NetworkError>
|
||||
func register(username: String, email: String, password: String) -> AnyPublisher<User, NetworkError>
|
||||
}
|
||||
|
||||
class NetworkUserRepository: UserRepository {
|
||||
let apiService: BudgetApiService
|
||||
|
||||
init(_ apiService: BudgetApiService) {
|
||||
self.apiService = apiService
|
||||
}
|
||||
|
||||
func getUser(id: Int) -> AnyPublisher<User, NetworkError> {
|
||||
return apiService.getUser(id: id)
|
||||
}
|
||||
|
||||
func searchUsers(withUsername: String) -> AnyPublisher<[User], NetworkError> {
|
||||
return apiService.searchUsers(query: withUsername)
|
||||
}
|
||||
|
||||
func login(username: String, password: String) -> AnyPublisher<User, NetworkError> {
|
||||
return apiService.login(username: username, password: password)
|
||||
}
|
||||
|
||||
func register(username: String, email: String, password: String) -> AnyPublisher<User, NetworkError> {
|
||||
return apiService.register(username: username, email: email, password: password)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
class MockUserRepository: UserRepository {
|
||||
func getUser(id: Int) -> AnyPublisher<User, NetworkError> {
|
||||
return Result<User, NetworkError>.Publisher(.failure(NetworkError.unknown))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func searchUsers(withUsername: String) -> AnyPublisher<[User], NetworkError> {
|
||||
return Result<[User], NetworkError>.Publisher(.failure(NetworkError.unknown))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func login(username: String, password: String) -> AnyPublisher<User, NetworkError> {
|
||||
return Result<User, NetworkError>.Publisher(.failure(NetworkError.unknown))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func register(username: String, email: String, password: String) -> AnyPublisher<User, NetworkError> {
|
||||
return Result<User, NetworkError>.Publisher(.failure(NetworkError.unknown))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -36,6 +36,14 @@ struct LoadingView<Content>: View where Content: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct EmbeddedLoadingView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ActivityIndicator: UIViewRepresentable {
|
||||
|
||||
@Binding var isAnimating: Bool
|
Loading…
Reference in a new issue