Skip to content

Commit

Permalink
Add SwiftUI demo app
Browse files Browse the repository at this point in the history
  • Loading branch information
andrebocchini committed Dec 10, 2022
1 parent 6b53709 commit fdb111f
Show file tree
Hide file tree
Showing 19 changed files with 970 additions and 0 deletions.

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions SwiftYNAB-SwiftUI-demo/SwiftYNAB-SwiftUI-demo/Account.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// Account.swift
// SwiftYNAB-SwiftUI-demo
//
// Created by Andre Bocchini on 12/9/22.
//

import Foundation
import SwiftYNAB

struct Account {
enum Kind: String {
case autoLoan = "autoLoan"
case cash = "cash"
case checking = "checking"
case creditCard = "creditCard"
case mortgage = "mortgage"
case otherDebt = "otherDebt"
case savings = "savings"
case unknown

var display: String {
switch self {
case .autoLoan:
return "Auto Loan"
case .cash:
return "Cash"
case .checking:
return "Checking"
case .creditCard:
return "Credit Card"
case .mortgage:
return "Mortgage"
case .otherDebt:
return "Other Debt"
case .savings:
return "Savings"
case .unknown:
return "Unknown"
}
}
}

let id: String
let name: String
let kind: Kind
let balance: String
let closed: Bool

}

extension Account {
init(account: SwiftYNAB.Account, budget: Budget) {
self.id = account.id
self.name = account.name

if let kind = Kind(rawValue: account.type) {
self.kind = kind
} else {
self.kind = .unknown
}

let currencyFormatter = CurrencyFormatter(currencyFormat: budget.currencyFormat)
self.balance = currencyFormatter.currencyString(from: account.balance) ?? ""

self.closed = account.closed
}
}
31 changes: 31 additions & 0 deletions SwiftYNAB-SwiftUI-demo/SwiftYNAB-SwiftUI-demo/AccountRowView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// AccountRowView.swift
// SwiftYNAB-SwiftUI-demo
//
// Created by Andre Bocchini on 12/9/22.
//

import Foundation
import SwiftUI

struct AccountRowView: View {
let account: Account

var body: some View {
HStack(spacing: 8) {
VStack(spacing: 8) {
Text(account.name)
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Text(account.kind.display)
.font(.subheadline)
.frame(maxWidth: .infinity, alignment: .leading)
}
.frame(maxWidth: .infinity)

Text(account.balance)
.font(.subheadline)
.frame(alignment: .trailing)
}
}
}
47 changes: 47 additions & 0 deletions SwiftYNAB-SwiftUI-demo/SwiftYNAB-SwiftUI-demo/AccountsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// AccountsView.swift
// SwiftYNAB-SwiftUI-demo
//
// Created by Andre Bocchini on 12/9/22.
//

import Foundation
import SwiftUI

struct AccountsView: View {
@Binding var token: String

@State var viewModel: AccountsViewModelType

var body: some View {
VStack {
if viewModel.isLoading {
ProgressView()
.progressViewStyle(.circular)
.scaleEffect(2, anchor: .center)
} else if viewModel.isError {
Text("Error loading accounts!")
} else {
List(viewModel.accounts, id: \.id) { account in
NavigationLink {
TransactionsView(
token: $token,
viewModel: TransactionsViewModel(
budget: viewModel.budget,
account: account
)
)
} label: {
AccountRowView(account: account)
}
}
}
}
.navigationTitle("Accounts")
.onAppear {
Task {
await viewModel.fetchAccounts(using: token)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// AccountsViewModel.swift
// SwiftYNAB-SwiftUI-demo
//
// Created by Andre Bocchini on 12/9/22.
//

import Foundation
import SwiftYNAB

protocol AccountsViewModelType {
var isLoading: Bool { get }
var isError: Bool { get }
var accounts: [Account] { get }
var budget: Budget { get }

mutating func fetchAccounts(using token: String) async
}

struct AccountsViewModel: AccountsViewModelType {
private(set) var isLoading = false
private(set) var isError = false
private(set) var accounts: [Account] = []
private(set) var budget: Budget
}

extension AccountsViewModel {
@MainActor mutating func fetchAccounts(using token: String) async {
if !isLoading, !isError {
defer {
isLoading = false
}

isLoading = true
let ynab = YNAB(accessToken: token)

do {
let result = try await ynab.accounts.getAccounts(budgetId: budget.id)
accounts = result
.filter({ !$0.closed })
.map({ Account(account: $0, budget: budget) })

isError = false
} catch {
isError = true
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
23 changes: 23 additions & 0 deletions SwiftYNAB-SwiftUI-demo/SwiftYNAB-SwiftUI-demo/Budget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Budget.swift
// SwiftYNAB-SwiftUI-demo
//
// Created by Andre Bocchini on 12/9/22.
//

import Foundation
import SwiftYNAB

struct Budget: Identifiable {
let id: String
let name: String
let currencyFormat: CurrencyFormat
}

extension Budget {
init(summary: BudgetSummary) {
self.id = summary.id
self.name = summary.name
self.currencyFormat = summary.currencyFormat
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// BudgetViewModel.swift
// SwiftYNAB-SwiftUI-demo
//
// Created by Andre Bocchini on 12/9/22.
//

import Foundation
import SwiftYNAB

protocol BudgetViewModelType {
var isLoading: Bool { get }
var isError: Bool { get }
var budgets: [Budget] { get }

mutating func fetchBudgets(using token: String) async
}

struct BudgetViewModel: BudgetViewModelType {
private(set) var isLoading = false
private(set) var isError = false
private(set) var budgets: [Budget] = []
}

extension BudgetViewModel {
@MainActor mutating func fetchBudgets(using token: String) async {
if !isLoading, !isError {
defer {
isLoading = false
}

isLoading = true
let ynab = YNAB(accessToken: token)

do {
let result = try await ynab.budgets.getBudgets()
budgets = result.map({ Budget(summary: $0) })

isError = false
} catch {
isError = true
}
}
}
}
41 changes: 41 additions & 0 deletions SwiftYNAB-SwiftUI-demo/SwiftYNAB-SwiftUI-demo/BudgetsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// BudgetsView.swift
// SwiftYNAB-SwiftUI-demo
//
// Created by Andre Bocchini on 12/8/22.
//

import Foundation
import SwiftUI

struct BudgetsView: View {
@Binding var token: String

@State var viewModel: BudgetViewModelType = BudgetViewModel()

var body: some View {
VStack {
if viewModel.isLoading {
ProgressView()
.progressViewStyle(.circular)
.scaleEffect(2, anchor: .center)
} else if viewModel.isError {
Text("Error loading budgets!")
} else {
List(viewModel.budgets, id: \.id) { budget in
NavigationLink {
AccountsView(token: $token, viewModel: AccountsViewModel(budget: budget))
} label: {
Text(budget.name)
}
}
}
}
.navigationTitle("Budgets")
.onAppear {
Task {
await viewModel.fetchBudgets(using: token)
}
}
}
}
Loading

0 comments on commit fdb111f

Please sign in to comment.