Compare commits
5 Commits
main
...
947555a816
| Author | SHA1 | Date | |
|---|---|---|---|
| 947555a816 | |||
| 6a53982a82 | |||
| af2af3d07a | |||
| 0573b07e1c | |||
| de58ebd79c |
@@ -14,6 +14,7 @@
|
|||||||
D40CCAEF2C2DC5D8007C4A9F /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40CCAEE2C2DC5D8007C4A9F /* Subscription.swift */; };
|
D40CCAEF2C2DC5D8007C4A9F /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40CCAEE2C2DC5D8007C4A9F /* Subscription.swift */; };
|
||||||
D40CCAF32C2EE305007C4A9F /* AddSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40CCAF22C2EE304007C4A9F /* AddSubscriptionView.swift */; };
|
D40CCAF32C2EE305007C4A9F /* AddSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40CCAF22C2EE304007C4A9F /* AddSubscriptionView.swift */; };
|
||||||
D426C55A2C2F0F150057455D /* PaymentCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D426C5592C2F0F150057455D /* PaymentCalendarView.swift */; };
|
D426C55A2C2F0F150057455D /* PaymentCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D426C5592C2F0F150057455D /* PaymentCalendarView.swift */; };
|
||||||
|
D43587412C3450F300DD321B /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43587402C3450F300DD321B /* Helper.swift */; };
|
||||||
D4544FEF2C320AF30090E311 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4544FEE2C320AF30090E311 /* HomeView.swift */; };
|
D4544FEF2C320AF30090E311 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4544FEE2C320AF30090E311 /* HomeView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
D40CCAEE2C2DC5D8007C4A9F /* Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subscription.swift; sourceTree = "<group>"; };
|
D40CCAEE2C2DC5D8007C4A9F /* Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subscription.swift; sourceTree = "<group>"; };
|
||||||
D40CCAF22C2EE304007C4A9F /* AddSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSubscriptionView.swift; sourceTree = "<group>"; };
|
D40CCAF22C2EE304007C4A9F /* AddSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSubscriptionView.swift; sourceTree = "<group>"; };
|
||||||
D426C5592C2F0F150057455D /* PaymentCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentCalendarView.swift; sourceTree = "<group>"; };
|
D426C5592C2F0F150057455D /* PaymentCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentCalendarView.swift; sourceTree = "<group>"; };
|
||||||
|
D43587402C3450F300DD321B /* Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helper.swift; sourceTree = "<group>"; };
|
||||||
D4544FEE2C320AF30090E311 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
D4544FEE2C320AF30090E311 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -66,6 +68,7 @@
|
|||||||
D40CCAE32C2DC5AA007C4A9F /* Assets.xcassets */,
|
D40CCAE32C2DC5AA007C4A9F /* Assets.xcassets */,
|
||||||
D40CCAE52C2DC5AA007C4A9F /* AboTracker.entitlements */,
|
D40CCAE52C2DC5AA007C4A9F /* AboTracker.entitlements */,
|
||||||
D40CCAE62C2DC5AA007C4A9F /* Preview Content */,
|
D40CCAE62C2DC5AA007C4A9F /* Preview Content */,
|
||||||
|
D43587402C3450F300DD321B /* Helper.swift */,
|
||||||
);
|
);
|
||||||
path = AboTracker;
|
path = AboTracker;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -165,6 +168,7 @@
|
|||||||
D4544FEF2C320AF30090E311 /* HomeView.swift in Sources */,
|
D4544FEF2C320AF30090E311 /* HomeView.swift in Sources */,
|
||||||
D426C55A2C2F0F150057455D /* PaymentCalendarView.swift in Sources */,
|
D426C55A2C2F0F150057455D /* PaymentCalendarView.swift in Sources */,
|
||||||
D40CCAE02C2DC5A9007C4A9F /* AboTrackerApp.swift in Sources */,
|
D40CCAE02C2DC5A9007C4A9F /* AboTrackerApp.swift in Sources */,
|
||||||
|
D43587412C3450F300DD321B /* Helper.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Bucket
|
||||||
|
uuid = "21F11B35-51F5-45D2-B767-D9510D0FF6A9"
|
||||||
|
type = "1"
|
||||||
|
version = "2.0">
|
||||||
|
</Bucket>
|
||||||
@@ -13,5 +13,6 @@ struct AboTrackerApp: App {
|
|||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView()
|
ContentView()
|
||||||
}
|
}
|
||||||
|
.modelContainer(for: [Subscription.self, Payment.self])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
AboTracker/Helper.swift
Normal file
17
AboTracker/Helper.swift
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/*func getMockedSubs() -> [Subscription] {
|
||||||
|
return [
|
||||||
|
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: getDate(from: "2023-01-01"))], color: .blue),
|
||||||
|
Subscription(name: "Fitness First", payments: [
|
||||||
|
Payment(amount: 7.9, intervall: .weekly, startDate: getDate(from: "2023-01-23")),
|
||||||
|
Payment(amount: 29, intervall: .quarter, startDate: getDate(from: "2023-04-03"))
|
||||||
|
], color: .red)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getDate(from dateString: String) -> Date {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd"
|
||||||
|
return formatter.date(from: dateString) ?? Date()
|
||||||
|
}*/
|
||||||
@@ -1,16 +1,38 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
import UIKit
|
||||||
|
|
||||||
final class Subscription: Identifiable {
|
@Model final class Subscription: Identifiable {
|
||||||
public let id = UUID()
|
@Attribute(.unique) public let id = UUID()
|
||||||
var name: String
|
var name: String
|
||||||
var payments: [Payment]
|
@Relationship(deleteRule: .cascade, minimumModelCount: 1) var payments: [Payment]
|
||||||
var color: Color
|
var colorData: Data?
|
||||||
|
|
||||||
init(name: String, payments: [Payment], color: Color) {
|
init(name: String, payments: [Payment], color: Color) {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.payments = payments
|
self.payments = payments
|
||||||
self.color = color
|
self.colorData = try? NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setColor(c: Color) {
|
||||||
|
colorData = try? NSKeyedArchiver.archivedData(withRootObject: c, requiringSecureCoding: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getColor() -> Color? {
|
||||||
|
guard let colorData = self.colorData else { return nil }
|
||||||
|
do {
|
||||||
|
if let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorData) {
|
||||||
|
return Color(color)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("Error unarchiving color data: \(error)")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getColorOrAccent() -> Color {
|
||||||
|
return getColor() ?? Color.accentColor
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMonthlyAmount() -> Float {
|
func getMonthlyAmount() -> Float {
|
||||||
@@ -94,8 +116,8 @@ final class Subscription: Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Payment: Identifiable {
|
@Model final class Payment: Identifiable {
|
||||||
public let id = UUID()
|
@Attribute(.unique) public let id = UUID()
|
||||||
var amount: Float
|
var amount: Float
|
||||||
var intervall: PaymentIntervall
|
var intervall: PaymentIntervall
|
||||||
var startDate: Date
|
var startDate: Date
|
||||||
@@ -107,7 +129,7 @@ final class Payment: Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Currency: CustomStringConvertible {
|
enum Currency: Codable, CustomStringConvertible {
|
||||||
case euro
|
case euro
|
||||||
case dollar
|
case dollar
|
||||||
|
|
||||||
@@ -121,7 +143,7 @@ enum Currency: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PaymentIntervall: CustomStringConvertible {
|
enum PaymentIntervall: Codable, CustomStringConvertible {
|
||||||
case weekly
|
case weekly
|
||||||
case monthly
|
case monthly
|
||||||
case quarter
|
case quarter
|
||||||
@@ -140,3 +162,22 @@ enum PaymentIntervall: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Color {
|
||||||
|
var hexString: String? {
|
||||||
|
guard let components = UIColor(self).cgColor.components, components.count >= 3 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = Float(components[0])
|
||||||
|
let g = Float(components[1])
|
||||||
|
let b = Float(components[2])
|
||||||
|
let a = Float(components.count == 4 ? components[3] : 1.0)
|
||||||
|
|
||||||
|
return String(format: "%02lX%02lX%02lX%02lX",
|
||||||
|
lroundf(r * 255),
|
||||||
|
lroundf(g * 255),
|
||||||
|
lroundf(b * 255),
|
||||||
|
lroundf(a * 255))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
struct AddSubscriptionView: View {
|
struct AddSubscriptionView: View {
|
||||||
@Environment(\.presentationMode) var presentationMode
|
@Environment(\.presentationMode) var presentationMode
|
||||||
@Binding var subs: [Subscription]
|
@Environment(\.modelContext) private var modelContext
|
||||||
|
@Binding var subscription: Subscription?
|
||||||
|
|
||||||
@State private var name: String = ""
|
@State private var name: String = ""
|
||||||
@State private var payments: [Payment] = [Payment(amount: 0, intervall: .monthly, startDate: Date())]
|
@State private var payments: [Payment] = [Payment(amount: 0, intervall: .monthly, startDate: Date())]
|
||||||
@@ -22,7 +24,7 @@ struct AddSubscriptionView: View {
|
|||||||
ForEach($payments) { $payment in
|
ForEach($payments) { $payment in
|
||||||
Section(header: Text("Payment")) {
|
Section(header: Text("Payment")) {
|
||||||
HStack {
|
HStack {
|
||||||
TextField("Amount", value: $payment.amount, formatter: NumberFormatter())
|
TextField("Amount", value: $payment.amount, format: .number)
|
||||||
Spacer()
|
Spacer()
|
||||||
Text("\(Currency.euro.description)")
|
Text("\(Currency.euro.description)")
|
||||||
}
|
}
|
||||||
@@ -47,14 +49,27 @@ struct AddSubscriptionView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Button("Add Subscription") {
|
Button(subscription == nil ? "Add Subscription" : "Save Subscription") {
|
||||||
let newSubscription = Subscription(name: name, payments: payments, color: color)
|
if let existingSubscription = subscription {
|
||||||
subs.append(newSubscription)
|
existingSubscription.name = name
|
||||||
|
existingSubscription.payments = payments
|
||||||
|
existingSubscription.setColor(c: color)
|
||||||
|
} else {
|
||||||
|
let newSubscription = Subscription(name: name, payments: payments, color: color)
|
||||||
|
modelContext.insert(newSubscription)
|
||||||
|
}
|
||||||
presentationMode.wrappedValue.dismiss()
|
presentationMode.wrappedValue.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Add Subscription")
|
.navigationTitle(subscription == nil ? "Add Subscription" : "Edit Subscription")
|
||||||
|
.onAppear {
|
||||||
|
if let existingSubscription = subscription {
|
||||||
|
name = existingSubscription.name
|
||||||
|
payments = existingSubscription.payments
|
||||||
|
color = existingSubscription.getColor() ?? .blue
|
||||||
|
}
|
||||||
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.navigationBarItems(trailing: Button("Cancel") {
|
.navigationBarItems(trailing: Button("Cancel") {
|
||||||
presentationMode.wrappedValue.dismiss()
|
presentationMode.wrappedValue.dismiss()
|
||||||
|
|||||||
@@ -1,27 +1,18 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@State private var subs: [Subscription] = []
|
@Query var subs: [Subscription]
|
||||||
|
|
||||||
init() {
|
|
||||||
self._subs = State(initialValue: [
|
|
||||||
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: getDate(from: "2023-01-01"))], color: .blue),
|
|
||||||
Subscription(name: "Fitness First", payments: [
|
|
||||||
Payment(amount: 7.9, intervall: .weekly, startDate: getDate(from: "2023-01-23")),
|
|
||||||
Payment(amount: 29, intervall: .quarter, startDate: getDate(from: "2023-04-03"))
|
|
||||||
], color: .red)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TabView {
|
TabView {
|
||||||
HomeView(subs: $subs)
|
HomeView()
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Image(systemName: "house.fill")
|
Image(systemName: "house.fill")
|
||||||
Text("Home")
|
Text("Home")
|
||||||
}
|
}
|
||||||
|
|
||||||
PaymentCalendarView(subs: $subs)
|
PaymentCalendarView()
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Image(systemName: "calendar")
|
Image(systemName: "calendar")
|
||||||
Text("Calendar")
|
Text("Calendar")
|
||||||
@@ -30,15 +21,11 @@ struct ContentView: View {
|
|||||||
.background(Color.gray.opacity(0.2))
|
.background(Color.gray.opacity(0.2))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getDate(from dateString: String) -> Date {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateFormat = "yyyy-MM-dd"
|
|
||||||
return formatter.date(from: dateString) ?? Date()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
struct ContentView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ContentView()
|
ContentView()
|
||||||
|
.modelContainer(for: [Subscription.self, Payment.self])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
struct HomeView: View {
|
struct HomeView: View {
|
||||||
@Binding var subs: [Subscription]
|
@Environment(\.modelContext) private var modelContext
|
||||||
|
@Query var subs: [Subscription]
|
||||||
@State private var showAddSubscriptionSheet = false
|
@State private var showAddSubscriptionSheet = false
|
||||||
|
@State private var selectedSubscription: Subscription?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
VStack {
|
VStack {
|
||||||
@@ -25,8 +28,12 @@ struct HomeView: View {
|
|||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
}
|
}
|
||||||
.listRowBackground(sub.color.opacity(0.2))
|
.listRowBackground(sub.getColorOrAccent().opacity(0.2))
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
|
.onTapGesture {
|
||||||
|
selectedSubscription = sub
|
||||||
|
showAddSubscriptionSheet = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onDelete(perform: deleteSubscription)
|
.onDelete(perform: deleteSubscription)
|
||||||
|
|
||||||
@@ -54,6 +61,7 @@ struct HomeView: View {
|
|||||||
}
|
}
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
|
selectedSubscription = nil
|
||||||
showAddSubscriptionSheet.toggle()
|
showAddSubscriptionSheet.toggle()
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
@@ -62,6 +70,7 @@ struct HomeView: View {
|
|||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
|
selectedSubscription = nil
|
||||||
showAddSubscriptionSheet.toggle()
|
showAddSubscriptionSheet.toggle()
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
@@ -70,14 +79,17 @@ struct HomeView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showAddSubscriptionSheet) {
|
.sheet(isPresented: $showAddSubscriptionSheet) {
|
||||||
AddSubscriptionView(subs: $subs)
|
AddSubscriptionView(subscription: $selectedSubscription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteSubscription(at offsets: IndexSet) {
|
func deleteSubscription(at offsets: IndexSet) {
|
||||||
subs.remove(atOffsets: offsets)
|
for index in offsets {
|
||||||
|
let itemToDelete = subs[index]
|
||||||
|
modelContext.delete(itemToDelete)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMonthlyTotal(subs: [Subscription]) -> Float {
|
func getMonthlyTotal(subs: [Subscription]) -> Float {
|
||||||
@@ -99,12 +111,7 @@ struct HomeView: View {
|
|||||||
|
|
||||||
struct HomeView_Previews: PreviewProvider {
|
struct HomeView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
HomeView(subs: .constant([
|
HomeView()
|
||||||
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: Date())], color: .blue),
|
.modelContainer(for: [Subscription.self, Payment.self])
|
||||||
Subscription(name: "Fitness First", payments: [
|
|
||||||
Payment(amount: 7.9, intervall: .weekly, startDate: Date()),
|
|
||||||
Payment(amount: 29, intervall: .quarter, startDate: Date())
|
|
||||||
], color: .red)
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
struct PaymentCalendarView: View {
|
struct PaymentCalendarView: View {
|
||||||
@Binding var subs: [Subscription]
|
@Query var subs: [Subscription]
|
||||||
let calendar = Calendar.current
|
let calendar = Calendar.current
|
||||||
let dateFormatter = DateFormatter()
|
let dateFormatter = DateFormatter()
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ struct PaymentCalendarView: View {
|
|||||||
for sub in subs {
|
for sub in subs {
|
||||||
for payment in sub.payments {
|
for payment in sub.payments {
|
||||||
if isPaymentDue(payment: payment, for: date) {
|
if isPaymentDue(payment: payment, for: date) {
|
||||||
return sub.color.opacity(0.3)
|
return sub.getColorOrAccent().opacity(0.3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,12 +153,7 @@ struct PaymentCalendarView: View {
|
|||||||
|
|
||||||
struct PaymentCalendarView_Previews: PreviewProvider {
|
struct PaymentCalendarView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
PaymentCalendarView(subs: .constant([
|
PaymentCalendarView()
|
||||||
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: Date())], color: .blue),
|
.modelContainer(for: [Subscription.self, Payment.self])
|
||||||
Subscription(name: "Fitness First", payments: [
|
|
||||||
Payment(amount: 7.9, intervall: .weekly, startDate: Date()),
|
|
||||||
Payment(amount: 29, intervall: .quarter, startDate: Date())
|
|
||||||
], color: .red)
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user