SwiftUI - Сохранение пользовательского класса как сущности Core Data? - PullRequest
0 голосов
/ 27 апреля 2020

Я работаю над приложением 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:

core data model

Мой вопрос заключается в том, как лучше всего получить базовые данные для распознавания моего пользовательские объекты? Какова лучшая практика здесь? Должен ли он полностью забыть объект Week и просто рассматривать объект WeeklyTimecard как одно и то же. Я чувствую, что у меня все настроено правильно, но мне просто не хватает направления, которое мне нужно, чтобы связать базовые данные с моими объектами.

...