import SwiftUI import UniformTypeIdentifiers import CoreData struct FuelLogImporterView: View { @Environment(\.managedObjectContext) private var viewContext @Environment(\.dismiss) var dismiss // Pass in the selected vehicle from FuelLogListView. var selectedVehicle: Vehicle? @State private var isFileImporterPresented = false @State private var importError: String? = nil var body: some View { VStack(spacing: 20) { Text("Import Fuel Logs from CSV") .font(.headline) Button("Select CSV File") { isFileImporterPresented = true } .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) if let error = importError { Text("Error: \(error)") .foregroundColor(.red) } } .fileImporter( isPresented: $isFileImporterPresented, allowedContentTypes: [UTType.commaSeparatedText], allowsMultipleSelection: false ) { result in switch result { case .success(let urls): if let url = urls.first { // Request access to the file URL. guard url.startAccessingSecurityScopedResource() else { importError = "Permission error: Unable to access the selected file." return } defer { url.stopAccessingSecurityScopedResource() } importCSV(from: url) } case .failure(let error): importError = error.localizedDescription } } .navigationTitle("Import Fuel Logs") } private func importCSV(from url: URL) { do { let data = try Data(contentsOf: url) guard let csvString = String(data: data, encoding: .utf8) else { importError = "Could not decode file." return } // Assume the first line is a header. let lines = csvString.components(separatedBy: "\n").filter { !$0.isEmpty } guard lines.count > 1 else { importError = "CSV file appears empty." return } // For this example, assume the CSV columns are: // date,odometer,fuelVolume,cost,locationCoordinates,locationName,octane,pricePerGalon,fullTank let header = lines[0].components(separatedBy: ",") for line in lines.dropFirst() { let fields = line.components(separatedBy: ",") if fields.count < header.count { print("Skipping row: \(line) – not enough fields (found \(fields.count), expected \(header.count))") continue } // Trim whitespace for each field let dateStr = fields[0].trimmingCharacters(in: .whitespacesAndNewlines) let odometerStr = fields[1].trimmingCharacters(in: .whitespacesAndNewlines) let fuelVolumeStr = fields[2].trimmingCharacters(in: .whitespacesAndNewlines) let costStr = fields[3].trimmingCharacters(in: .whitespacesAndNewlines) let locationCoordinates = fields[4].trimmingCharacters(in: .whitespacesAndNewlines) let locationName = fields[5].trimmingCharacters(in: .whitespacesAndNewlines) let octaneStr = fields[6].trimmingCharacters(in: .whitespacesAndNewlines) let pricePerGalonStr = fields[7].trimmingCharacters(in: .whitespacesAndNewlines) let fullTankStr = fields[8].trimmingCharacters(in: .whitespacesAndNewlines) // Debug prints for each field print("Row: \(line)") print("Date: \(dateStr), Odometer: \(odometerStr), Fuel Volume: \(fuelVolumeStr), Cost: \(costStr), Octane: \(octaneStr), Price/Gal: \(pricePerGalonStr), FullTank: \(fullTankStr)") // Setup date formatter – adjust format as needed. var parsedDate: Date? let formats = ["M/d/yyyy", "yyyy-MM-dd HH:mm"] for format in formats { let formatter = DateFormatter() formatter.dateFormat = format formatter.locale = Locale(identifier: "en_US_POSIX") if let date = formatter.date(from: dateStr) { parsedDate = date break } } guard let date = parsedDate else { print("Skipping row – invalid date: \(dateStr)") continue } guard let odometer = Double(odometerStr) else { print("Skipping row – invalid odometer: \(odometerStr)") continue } guard let fuelVolume = Double(fuelVolumeStr) else { print("Skipping row – invalid fuel volume: \(fuelVolumeStr)") continue } guard let cost = Double(costStr) else { print("Skipping row – invalid cost: \(costStr)") continue } guard let octane = Int16(octaneStr) else { print("Skipping row – invalid octane: \(octaneStr)") continue } guard let pricePerGalon = Double(pricePerGalonStr) else { print("Skipping row – invalid price per gallon: \(pricePerGalonStr)") continue } // If all conversions succeed, create the fuel log. let fullTank = (fullTankStr.lowercased() == "true" || fullTankStr == "1") let newLog = FuelLog(context: viewContext) newLog.id = UUID() newLog.date = date newLog.odometer = odometer newLog.fuelVolume = fuelVolume newLog.cost = cost newLog.locationCoordinates = locationCoordinates newLog.locationName = locationName newLog.octane = octane newLog.pricePerGalon = pricePerGalon newLog.fullTank = fullTank // Optionally assign vehicle if needed. print("Assigning vehicle: \(selectedVehicle?.make ?? "none")") newLog.vehicle = selectedVehicle print("Imported row successfully.") } try viewContext.save() dismiss() } catch { importError = error.localizedDescription } } } struct FuelLogImporterView_Previews: PreviewProvider { static var previews: some View { FuelLogImporterView(selectedVehicle: nil) } }