Я работаю над приложением timecard, встроенным в SwiftUI, чтобы упростить мне и моим коллегам заполнение расписаний для работы. В целом у меня все работает, кроме сохранения данных. У меня есть 3 пользовательских класса, которые содержат всю информацию для приложения: неделя, день, работа.
«Неделя» - это, по сути, временная карта. Он содержит набор объектов «День», а также некоторую другую информацию:
import Foundation
class Week: ObservableObject, Identifiable, Codable {
@Published var id: UUID
@Published var endDate: Date
@Published var days = [Day]()
@Published var sunday: Day
@Published var monday: Day
@Published var tuesday: Day
@Published var wednesday: Day
@Published var thursday: Day
@Published var friday: Day
@Published var saturday: Day
var totalHours: Double {
get {
var totalHours = 0.0
for day in days {
totalHours = totalHours + day.totalHours
}
return totalHours
}
}
var totalDollars: Double {
get {
var totalDollars = 0.0
for day in days {
totalDollars = totalDollars + day.totalDollars
}
return totalDollars
}
}
init(endDate: Date) {
id = UUID()
self.endDate = endDate
sunday = Day(name: "Sunday", date: endDate.subtractDays(days: 6))
monday = Day(name: "Monday", date: endDate.subtractDays(days: 5))
tuesday = Day(name: "Tuesday", date: endDate.subtractDays(days: 4))
wednesday = Day(name: "Wednesday", date: endDate.subtractDays(days: 3))
thursday = Day(name: "Thursday", date: endDate.subtractDays(days: 2))
friday = Day(name: "Friday", date: endDate.subtractDays(days: 1))
saturday = Day(name: "Saturday", date: endDate)
days = [sunday, monday, tuesday, wednesday, thursday, friday, saturday]
}
// Mark: - Codable Encoding & Decoding
enum CodingKeys: CodingKey {
case id
case endDate
case days
case sunday
case monday
case tuesday
case wednesday
case thursday
case friday
case saturday
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
endDate = try container.decode(Date.self, forKey: .endDate)
days = try container.decode([Day].self, forKey: .days)
sunday = try container.decode(Day.self, forKey: .sunday)
monday = try container.decode(Day.self, forKey: .monday)
tuesday = try container.decode(Day.self, forKey: .tuesday)
wednesday = try container.decode(Day.self, forKey: .wednesday)
thursday = try container.decode(Day.self, forKey: .thursday)
friday = try container.decode(Day.self, forKey: .friday)
saturday = try container.decode(Day.self, forKey: .saturday)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(endDate, forKey: .endDate)
try container.encode(days, forKey: .days)
try container.encode(sunday, forKey: .sunday)
try container.encode(monday, forKey: .monday)
try container.encode(tuesday, forKey: .tuesday)
try container.encode(wednesday, forKey: .wednesday)
try container.encode(thursday, forKey: .thursday)
try container.encode(friday, forKey: .friday)
try container.encode(saturday, forKey: .saturday)
}
}
Объект «День», список заданий на этот день, а также несколько других переменных:
import Foundation
public class Day: NSObject, ObservableObject, Codable {
var name: String
@Published var date: Date
@Published var jobs: [Job] = []
var totalHours: Double {
get {
var totalHours = 0.0
for job in jobs {
totalHours = totalHours + job.totalHours
}
return totalHours
}
}
var totalDollars: Double {
get {
var totalDollars = 0.0
for job in jobs {
totalDollars = totalDollars + job.totalDollars
}
return totalDollars
}
}
init(name: String, date: Date) {
self.name = name
self.date = date
}
func add(job: Job) {
jobs.append(job)
}
// Mark: - Codable Encoding & Decoding
enum CodingKeys: CodingKey {
case date
case jobs
case name
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
date = try container.decode(Date.self, forKey: .date)
jobs = try container.decode([Job].self, forKey: .jobs)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(date, forKey: .date)
try container.encode(jobs, forKey: .jobs)
}
}
И, наконец, объект "Job" содержит указанную c информацию, такую как сведения о работе, отработанные часы и скорость:
public class Job: NSObject, ObservableObject, Identifiable, Codable {
@Published public var id: UUID
@Published var projectFullName: String = ""
@Published var projectShortName: String = ""
@Published var jobNumber: String = ""
@Published var position: String = ""
@Published var rateString: String = ""
@Published var startTime: Date
@Published var endTime: Date
@Published var firstBreakStartTime: Date
@Published var firstBreakEndTime: Date
@Published var secondBreakStartTime: Date
@Published var secondBreakEndTime: Date
@Published var tookFirstBreak: Bool
@Published var tookSecondBreak: Bool
var rate: Double {
get {
let rate = Double(rateString) ?? 0
return rate
}
}
var firstBreakTotal: Double {
get {
let timeElapsed = self.firstBreakEndTime.timeIntervalSince(self.firstBreakStartTime)
let totalHours = timeElapsed / 3600
return totalHours
}
}
var secondBreakTotal: Double {
get {
let timeElapsed = self.secondBreakEndTime.timeIntervalSince(self.secondBreakStartTime)
let totalHours = timeElapsed / 3600
return totalHours
}
}
var totalHours: Double {
get {
let timeElapsed = self.endTime.timeIntervalSince(self.startTime)
var totalHours = timeElapsed / 3600
if tookFirstBreak {
totalHours = totalHours - firstBreakTotal
}
if tookSecondBreak {
totalHours = totalHours - secondBreakTotal
}
return totalHours
}
}
var standardTimeHours: Double {
get {
var hours = totalHours
if hours > 8 {
hours = 8
}
return hours
}
}
var timeAndAHalfHours: Double {
get {
var hours = totalHours
if hours > 8 {
hours = hours - 8
if hours > 5 {
hours = 5
}
} else {
hours = 0
}
return hours
}
}
var doubleTimeHours: Double {
get {
var hours = totalHours
if hours > 8 {
hours = hours - 8
if hours > 5 {
hours = hours - 5
} else {
hours = 0
}
} else {
hours = 0
}
return hours
}
}
var standardTimeDollars: Double {
get {
let total = rate * standardTimeHours
return total
}
}
var timeAndAHalfDollars: Double {
get {
let changedRate = rate * 1.5
let total = changedRate * timeAndAHalfHours
return total
}
}
var doubleTimeDollars: Double {
get {
let changedRate = rate * 2
let total = changedRate * doubleTimeHours
return total
}
}
var totalDollars: Double {
get {
let total = standardTimeDollars + timeAndAHalfDollars + doubleTimeDollars
return total
}
}
// Mark: - Init Methods
init(date: Date) {
id = UUID()
startTime = date.defaultStartTime()
endTime = date.defaultEndTime()
firstBreakStartTime = date.defaultFirstBreakStartTime()
firstBreakEndTime = date.defaultFirstBreakEndTime()
secondBreakStartTime = date.defaultSecondBreakStartTime()
secondBreakEndTime = date.defaultSecondBreakEndTime()
tookFirstBreak = true
tookSecondBreak = false
}
init(id: UUID, date: Date, projectFullName: String, projectShortName: String, jobNumber: String, position: String, rateString: String) {
self.id = UUID()
startTime = date.defaultStartTime()
endTime = date.defaultEndTime()
firstBreakStartTime = date.defaultFirstBreakStartTime()
firstBreakEndTime = date.defaultFirstBreakEndTime()
secondBreakStartTime = date.defaultSecondBreakStartTime()
secondBreakEndTime = date.defaultSecondBreakEndTime()
self.projectFullName = projectFullName
self.projectShortName = projectShortName
self.jobNumber = jobNumber
self.position = position
self.rateString = rateString
tookFirstBreak = true
tookSecondBreak = false
}
// Mark: - Codable Encoding & Decoding
enum CodingKeys: CodingKey {
case id
case projectFullName
case projectShortName
case jobNumber
case position
case rateString
case startTime
case endTime
case firstBreakStartTime
case firstBreakEndTime
case secondBreakStartTime
case secondBreakEndTime
case tookFirstBreak
case tookSecondBreak
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
projectFullName = try container.decode(String.self, forKey: .projectFullName)
projectShortName = try container.decode(String.self, forKey: .projectShortName)
jobNumber = try container.decode(String.self, forKey: .jobNumber)
position = try container.decode(String.self, forKey: .position)
rateString = try container.decode(String.self, forKey: .rateString)
startTime = try container.decode(Date.self, forKey: .startTime)
endTime = try container.decode(Date.self, forKey: .endTime)
firstBreakStartTime = try container.decode(Date.self, forKey: .firstBreakStartTime)
firstBreakEndTime = try container.decode(Date.self, forKey: .firstBreakEndTime)
secondBreakStartTime = try container.decode(Date.self, forKey: .secondBreakStartTime)
secondBreakEndTime = try container.decode(Date.self, forKey: .secondBreakEndTime)
tookFirstBreak = try container.decode(Bool.self, forKey: .tookFirstBreak)
tookSecondBreak = try container.decode(Bool.self, forKey: .tookSecondBreak)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(projectFullName, forKey: .projectFullName)
try container.encode(projectShortName, forKey: .projectShortName)
try container.encode(jobNumber, forKey: .jobNumber)
try container.encode(position, forKey: .position)
try container.encode(rateString, forKey: .rateString)
try container.encode(startTime, forKey: .startTime)
try container.encode(endTime, forKey: .endTime)
try container.encode(firstBreakStartTime, forKey: .firstBreakStartTime)
try container.encode(firstBreakEndTime, forKey: .firstBreakEndTime)
try container.encode(secondBreakStartTime, forKey: .secondBreakStartTime)
try container.encode(secondBreakEndTime, forKey: .secondBreakEndTime)
try container.encode(tookFirstBreak, forKey: .tookFirstBreak)
try container.encode(tookSecondBreak, forKey: .tookSecondBreak)
}
}
Поскольку все классы являются Codable, я смог получить кодировать и декодировать в Plist, чтобы сохранить его, но я подумал, что CoreData может быть лучшим способом для go в конце. Я настроил свою модель для эмуляции объекта Week:
Мой вопрос заключается в том, как лучше всего получить базовые данные для распознавания моего пользовательские объекты? Какова лучшая практика здесь? Должен ли он полностью забыть объект Week и просто рассматривать объект WeeklyTimecard как одно и то же. Я чувствую, что у меня все настроено правильно, но мне просто не хватает направления, которое мне нужно, чтобы связать базовые данные с моими объектами.