Skip to content

Commit

Permalink
add cloudkit support (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
corybuecker authored Jan 17, 2024
1 parent e1075bf commit 4eb9946
Show file tree
Hide file tree
Showing 22 changed files with 644 additions and 41 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,6 @@ iOSInjectionProject/

SimpleBudget.xcodeproj
buildServer.json
.compile
Sources/Info.plist
Sources/simple-budget.entitlements
16 changes: 0 additions & 16 deletions Info.plist

This file was deleted.

20 changes: 20 additions & 0 deletions Sources/Assets.xcassets/AccentColor.colorset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.227",
"green" : "0.160",
"red" : "0.127"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
14 changes: 14 additions & 0 deletions Sources/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "piggy_bank_icon_1024x1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions Sources/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
24 changes: 19 additions & 5 deletions Sources/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import SwiftData
import SwiftUI

struct ContentView: View {
init() {
print("test")
}
@Environment(\.modelContext) var context: ModelContext

var body: some View {
return VStack {
Text("Hello, World!")
TabView {
Reports()
.tabItem {
Label("Reports", systemImage: "chart.bar")
}
AccountList()
.tabItem {
Label("Accounts", systemImage: "building.columns")
}
SavingList()
.tabItem {
Label("Savings", systemImage: "dollarsign.circle")
}
GoalList()
.tabItem {
Label("Goals", systemImage: "target")
}
}
}
}
14 changes: 14 additions & 0 deletions Sources/Models/Account.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import SwiftData

@Model
class Account {
var name: String = ""
var balance: Double = 0.0
var debt: Bool = false

init(name: String, balance: Double, debt: Bool) {
self.name = name
self.balance = balance
self.debt = debt
}
}
24 changes: 24 additions & 0 deletions Sources/Models/Goal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import SwiftData
import SwiftUI

enum GoalRecurrence: Codable {
case daily
case weekly
case monthly
case yearly
}

@Model
class Goal {
var name: String = ""
var amount: Double = 0.0
var recurrence: GoalRecurrence = GoalRecurrence.monthly
var targetDate: Date = Date()

init(name: String, amount: Double, recurrence: GoalRecurrence, targetDate: Date) {
self.name = name
self.amount = amount
self.recurrence = recurrence
self.targetDate = targetDate
}
}
12 changes: 12 additions & 0 deletions Sources/Models/Saving.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import SwiftData

@Model
class Saving {
var name: String = ""
var amount: Double = 0.0

init(name: String, amount: Double) {
self.name = name
self.amount = amount
}
}
92 changes: 92 additions & 0 deletions Sources/Services/DateService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import SwiftUI

struct DateService {
enum DateServiceError: Error {
case unexpectedDate
}

private let from: Date
private let calendar: Calendar

init() {
self.from = Date()
self.calendar = Calendar.current
}

init(_ from: Date) {
self.from = from
self.calendar = Calendar.current
}

func startOfMonth() throws -> Date {
let components = self.calendar.dateComponents([.year, .month], from: self.from)
guard let optDate = self.calendar.date(from: components) else {
throw DateServiceError.unexpectedDate
}
return optDate
}

func endOfMonth() throws -> Date {
let startOfMonth = try startOfMonth()
let components = DateComponents(month: 1, second: -1)
guard let endOfMonth = self.calendar.date(byAdding: components, to: startOfMonth) else {
throw DateServiceError.unexpectedDate
}
return endOfMonth
}

func startOfNextMonth() throws -> Date {
let addComponents = DateComponents(month: 1)

guard let nextMonth = self.calendar.date(byAdding: addComponents, to: self.from) else {
throw DateServiceError.unexpectedDate
}

let nextMonthComponents = self.calendar.dateComponents([.year, .month], from: nextMonth)

guard let optDate = self.calendar.date(from: nextMonthComponents) else {
throw DateServiceError.unexpectedDate
}
return optDate
}

func endOfNextMonth() throws -> Date {
let startOfNextMonth = try startOfNextMonth()
let components = DateComponents(month: 1, second: -1)
guard let endOfNextMonth = self.calendar.date(byAdding: components, to: startOfNextMonth) else {
throw DateServiceError.unexpectedDate
}
return endOfNextMonth
}

func isEndOfMonth() throws -> Bool {
let dateComponents = self.calendar.dateComponents([.year, .month, .day], from: self.from)
let endOfMonthComponents = try self.calendar.dateComponents(
[.year, .month, .day], from: endOfMonth())

return dateComponents.year == endOfMonthComponents.year
&& dateComponents.month == endOfMonthComponents.month
&& dateComponents.day == endOfMonthComponents.day
}

func daysUntilEndOfMonth() throws -> Int {
if try isEndOfMonth() {
guard
let days = try self.calendar.dateComponents(
[.day], from: self.from, to: endOfNextMonth()
).day
else {
throw DateServiceError.unexpectedDate
}
return days
}

guard
let days = try self.calendar.dateComponents([.day], from: self.from, to: endOfMonth())
.day
else {
throw DateServiceError.unexpectedDate
}
return days
}
}
52 changes: 52 additions & 0 deletions Sources/Services/GoalService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import SwiftUI

struct GoalService {
private let goal: Goal
private let day: Double = 60 * 60 * 24.0

init(goal: Goal) {
self.goal = goal
}

func amortized() -> Decimal {
if Date() >= self.goal.targetDate {
return Decimal(self.goal.amount)
}

return dailyAmount() * Decimal(elapsedDays())
}

func dailyAmount() -> Decimal {
if Date() >= self.goal.targetDate {
return Decimal(0)
}

if Date() < startDate() {
return Decimal(0)
}

return Decimal(self.goal.amount)
/ Decimal(DateInterval(start: startDate(), end: self.goal.targetDate).duration / day)
}

private func elapsedDays() -> Double {
if Date() < startDate() {
return 0
}

return DateInterval(start: startDate(), end: Date()).duration / day
}

private func startDate() -> Date {
switch self.goal.recurrence {
case GoalRecurrence.daily:
return Calendar.current.date(byAdding: .day, value: -1, to: self.goal.targetDate)!
case GoalRecurrence.weekly:
return Calendar.current.date(byAdding: .weekOfYear, value: -1, to: self.goal.targetDate)!
case GoalRecurrence.monthly:
return Calendar.current.date(byAdding: .month, value: -1, to: self.goal.targetDate)!
case GoalRecurrence.yearly:
return Calendar.current.date(byAdding: .year, value: -1, to: self.goal.targetDate)!
}
}
}
9 changes: 5 additions & 4 deletions Sources/SimpleBudget.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import SwiftData
import SwiftUI

@main
struct SimpleBudget: App {
var body: some Scene {
WindowGroup {
ContentView()
}
var body: some Scene {
WindowGroup {
ContentView().modelContainer(for: [Account.self, Saving.self, Goal.self])
}
}
}
43 changes: 43 additions & 0 deletions Sources/Views/AccountForm.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import SwiftData
import SwiftUI

struct AccountForm: View {
let account: Account?

@State private var name: String = ""
@State private var balance: Double = 0.0
@State private var debt: Bool = false

@Environment(\.modelContext) var modelContext: ModelContext
@Environment(\.dismiss) private var dismiss

var body: some View {
Form {
TextField("Name", text: $name)
TextField("Balance", value: $balance, format: .number)
Picker("Debt", selection: $debt) {
Text("No").tag(false)
Text("Yes").tag(true)
}.pickerStyle(.segmented)
}.navigationBarTitle("New Account").toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
if let account {
account.name = name
account.balance = balance
account.debt = debt
} else {
modelContext.insert(Account(name: name, balance: balance, debt: debt))
}
dismiss()
}
}
}.onAppear {
if let account {
name = account.name
balance = account.balance
debt = account.debt
}
}
}
}
34 changes: 34 additions & 0 deletions Sources/Views/AccountList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import SwiftData
import SwiftUI

struct AccountList: View {
@Environment(\.modelContext) var modelContext: ModelContext
@Query<Account> private var accounts: [Account]

var body: some View {
VStack {
NavigationStack {
List {
ForEach(accounts, id: \.self) { account in
NavigationLink(destination: AccountForm(account: account)) {
Text(account.name)
}
}.onDelete(perform: deleteAccount)
}.navigationBarTitle("Accounts").toolbar {
ToolbarItem {
NavigationLink(destination: AccountForm(account: nil)) {
Image(systemName: "plus")
}
}
}
}
Spacer()
}
}

func deleteAccount(at offsets: IndexSet) {
for offset in offsets {
modelContext.delete(accounts[offset])
}
}
}
Loading

0 comments on commit 4eb9946

Please sign in to comment.