From d35ff04c41ccd4715cc9f72e7665c6c4397bfa8d Mon Sep 17 00:00:00 2001 From: himanshunaidu Date: Fri, 29 May 2026 15:26:39 -0700 Subject: [PATCH 1/3] Start adding suppot to prod environment in API requests, as well as local dataset generation --- .../LocalDataset/DatasetDecoder.swift | 8 ++- .../LocalDataset/DatasetEncoder.swift | 9 ++- .../Shared/SharedAppConstants.swift | 2 +- .../TDEI/Config/APIEnvironment.swift | 22 +++---- IOSAccessAssessment/View/SetupView.swift | 9 ++- .../View/TestMode/TestCameraView.swift | 6 +- .../View/TestMode/TestListView.swift | 60 +++++++++++++++---- 7 files changed, 87 insertions(+), 29 deletions(-) diff --git a/IOSAccessAssessment/LocalDataset/DatasetDecoder.swift b/IOSAccessAssessment/LocalDataset/DatasetDecoder.swift index 621f3367..5e2116df 100644 --- a/IOSAccessAssessment/LocalDataset/DatasetDecoder.swift +++ b/IOSAccessAssessment/LocalDataset/DatasetDecoder.swift @@ -54,8 +54,10 @@ struct DatasetCaptureData { Finally, it also adds a node to TDEI workspaces at the capture location. */ class DatasetDecoder { + private var apiEnvironment: APIEnvironment private var workspaceId: String + private var environmentDirectory: URL private var workspaceDirectory: URL private var datasetDirectory: URL var totalFrames: Int = 0 @@ -79,11 +81,13 @@ class DatasetDecoder { private let otherDetailsDecoder: OtherDetailsDecoder private let meshDecoder: MeshDecoder - init(workspaceId: String, changesetId: String) throws { + init(apiEnvironment: APIEnvironment, workspaceId: String, changesetId: String) throws { + self.apiEnvironment = apiEnvironment self.workspaceId = workspaceId + self.environmentDirectory = try DatasetDecoder.findDirectory(id: apiEnvironment.rawValue) /// Get workspace directory - self.workspaceDirectory = try DatasetDecoder.findDirectory(id: workspaceId) + self.workspaceDirectory = try DatasetDecoder.findDirectory(id: workspaceId, relativeTo: self.environmentDirectory) /// Get dataset directory self.datasetDirectory = try DatasetDecoder.findDirectory(id: changesetId, relativeTo: self.workspaceDirectory) diff --git a/IOSAccessAssessment/LocalDataset/DatasetEncoder.swift b/IOSAccessAssessment/LocalDataset/DatasetEncoder.swift index 9d743d20..9d7d036e 100644 --- a/IOSAccessAssessment/LocalDataset/DatasetEncoder.swift +++ b/IOSAccessAssessment/LocalDataset/DatasetEncoder.swift @@ -29,8 +29,10 @@ enum DatasetEncoderError: Error, LocalizedError { Finally, it also adds a node to TDEI workspaces at the capture location. */ class DatasetEncoder { + private var apiEnvironment: APIEnvironment private var workspaceId: String + private var environmentDirectory: URL private var workspaceDirectory: URL private var datasetDirectory: URL private var savedFrames: Int = 0 @@ -62,11 +64,14 @@ class DatasetEncoder { public var capturedFrameIds: Set = [] - init(workspaceId: String, changesetId: String) throws { + init(apiEnvironment: APIEnvironment, workspaceId: String, changesetId: String) throws { + self.apiEnvironment = apiEnvironment self.workspaceId = workspaceId /// Create workspace Directory if it doesn't exist - self.workspaceDirectory = try DatasetEncoder.createDirectory(id: workspaceId) + self.environmentDirectory = try DatasetEncoder.createDirectory(id: apiEnvironment.rawValue) + /// if environment directory exists, create workspace directory inside it + self.workspaceDirectory = try DatasetEncoder.createDirectory(id: workspaceId, relativeTo: self.environmentDirectory) /// if workspace directory exists, create dataset directory inside it self.datasetDirectory = try DatasetEncoder.createDirectory(id: changesetId, relativeTo: self.workspaceDirectory) diff --git a/IOSAccessAssessment/Shared/SharedAppConstants.swift b/IOSAccessAssessment/Shared/SharedAppConstants.swift index 5b8d2394..494bb53c 100644 --- a/IOSAccessAssessment/Shared/SharedAppConstants.swift +++ b/IOSAccessAssessment/Shared/SharedAppConstants.swift @@ -45,7 +45,7 @@ struct SharedAppConstants { } struct WorkspaceConstants { - static let primaryWorkspaceIds: [String] = ["1928"] + static let primaryWorkspaceIds: [String] = ["1940"] // ["1463"] // ["288", "349", "1411"] // "252", "322", "368", "374", "378", "381", "384", "323", "369", "156", "375", "379"] diff --git a/IOSAccessAssessment/TDEI/Config/APIEnvironment.swift b/IOSAccessAssessment/TDEI/Config/APIEnvironment.swift index e9f8c0ba..4ee6ad17 100644 --- a/IOSAccessAssessment/TDEI/Config/APIEnvironment.swift +++ b/IOSAccessAssessment/TDEI/Config/APIEnvironment.swift @@ -13,7 +13,7 @@ import Foundation enum APIEnvironment: String, CaseIterable, Sendable, Hashable { // case development = "Development" case staging = "Staging" -// case production = "Production" + case production = "Production" static var `default`: APIEnvironment { return .staging @@ -25,8 +25,8 @@ enum APIEnvironment: String, CaseIterable, Sendable, Hashable { // return "https://tdei-api-dev.azurewebsites.net/api/v1" case .staging: return "https://tdei-gateway-stage.azurewebsites.net/api/v1" -// case .production: -// return "https://tdei-gateway-prod.azurewebsites.net/api/v1" + case .production: + return "https://tdei-gateway-prod.azurewebsites.net/api/v1" } } @@ -36,8 +36,8 @@ enum APIEnvironment: String, CaseIterable, Sendable, Hashable { // return "https://api.workspaces-dev.sidewalks.washington.edu/api/v1" case .staging: return "https://api.workspaces-stage.sidewalks.washington.edu/api/v1" -// case .production: -// return "https://api.workspaces.sidewalks.washington.edu/api/v1" + case .production: + return "https://api.workspaces.sidewalks.washington.edu/api/v1" } } @@ -47,8 +47,8 @@ enum APIEnvironment: String, CaseIterable, Sendable, Hashable { // return "https://osm.workspaces-dev.sidewalks.washington.edu/api/0.6" case .staging: return "https://osm.workspaces-stage.sidewalks.washington.edu/api/0.6" -// case .production: -// return "https://osm.workspaces.sidewalks.washington.edu/api/0.6" + case .production: + return "https://osm.workspaces.sidewalks.washington.edu/api/0.6" } } @@ -58,8 +58,8 @@ enum APIEnvironment: String, CaseIterable, Sendable, Hashable { // return "https://tdei-usermanagement-be-dev.azurewebsites.net/api/v1" case .staging: return "https://tdei-usermanagement-stage.azurewebsites.net/api/v1" -// case .production: -// return "https://tdei-usermanagement-prod.azurewebsites.net/api/v1" + case .production: + return "https://tdei-usermanagement-prod.azurewebsites.net/api/v1" } } @@ -69,8 +69,8 @@ enum APIEnvironment: String, CaseIterable, Sendable, Hashable { // return "Development" case .staging: return "Staging" -// case .production: -// return "Production" + case .production: + return "Production" } } } diff --git a/IOSAccessAssessment/View/SetupView.swift b/IOSAccessAssessment/View/SetupView.swift index 3f9139ea..ff84a458 100644 --- a/IOSAccessAssessment/View/SetupView.swift +++ b/IOSAccessAssessment/View/SetupView.swift @@ -457,6 +457,7 @@ struct SetupView: View { private func openChangeset() { Task { do { + let selectedEnvironment = userStateViewModel.selectedEnvironment guard let workspaceId = workspaceViewModel.workspaceId else { throw SetupViewError.noWorkspaceId } @@ -470,7 +471,10 @@ struct SetupView: View { ) workspaceViewModel.updateChangeset(id: openedChangesetId) changesetOpenViewModel.isChangesetOpened = true - try initializeCurrentDataset(workspaceId: workspaceId, changeSetId: openedChangesetId) + try initializeCurrentDataset( + apiEnvironment: selectedEnvironment, + workspaceId: workspaceId, changeSetId: openedChangesetId + ) } catch SetupViewError.currentDatasetInitializationFailed { setCurrentDatasetStatusErrorHint(SetupViewError.currentDatasetInitializationFailed.localizedDescription) } catch { @@ -483,9 +487,10 @@ struct SetupView: View { } } - private func initializeCurrentDataset(workspaceId: String, changeSetId: String) throws { + private func initializeCurrentDataset(apiEnvironment: APIEnvironment, workspaceId: String, changeSetId: String) throws { do { sharedAppData.currentDatasetEncoder = try DatasetEncoder( + apiEnvironment: apiEnvironment, workspaceId: workspaceId, changesetId: changeSetId ) } catch { diff --git a/IOSAccessAssessment/View/TestMode/TestCameraView.swift b/IOSAccessAssessment/View/TestMode/TestCameraView.swift index 19afaf39..1e945659 100644 --- a/IOSAccessAssessment/View/TestMode/TestCameraView.swift +++ b/IOSAccessAssessment/View/TestMode/TestCameraView.swift @@ -103,6 +103,7 @@ class LocationManagerPlaceholder: NSObject, ObservableObject { */ struct TestCameraView: View { let selectedClasses: [AccessibilityFeatureClass] + let selectedEnvironment: APIEnvironment let workspaceId: String let changesetId: String @@ -328,7 +329,10 @@ struct TestCameraView: View { } private func initializeDatasetDecoder() throws -> DatasetDecoder { - return try DatasetDecoder(workspaceId: workspaceId, changesetId: changesetId) + return try DatasetDecoder( + apiEnvironment: selectedEnvironment, + workspaceId: workspaceId, changesetId: changesetId + ) } private func loadData(datasetDecoder: DatasetDecoder, enhancedAnalysisMode: Bool) throws -> DatasetCaptureData { diff --git a/IOSAccessAssessment/View/TestMode/TestListView.swift b/IOSAccessAssessment/View/TestMode/TestListView.swift index bdf258f6..d6aa16ce 100644 --- a/IOSAccessAssessment/View/TestMode/TestListView.swift +++ b/IOSAccessAssessment/View/TestMode/TestListView.swift @@ -23,19 +23,62 @@ enum TestListViewError: Error, LocalizedError { } } +struct TestEnvironmentListView: View { + let selectedClasses: [AccessibilityFeatureClass] + + @Environment(\.dismiss) var dismiss + + let datasetLister: DatasetLister = DatasetLister() + + var body: some View { + VStack { + HStack { + Text("Please select an environment dataset:") + .font(.subheadline) + .padding(.bottom, 5) + } + + if datasetLister.environmentDirectories.count > 0 { + List { + ForEach(datasetLister.environmentDirectories, id: \.self) { environmentDir in + NavigationLink( + destination: TestWorkspaceListView( + selectedClasses: selectedClasses, environmentDir: environmentDir, datasetLister: datasetLister + ) + ) { + Text(environmentDir.lastPathComponent) + .foregroundColor(.primary) + .cornerRadius(8) + } + } + } + } else { + Text("No environment datasets found.") + Spacer() + } + } + .navigationBarTitle("Test: Environment Selection", displayMode: .inline) + .onAppear { + do { + try datasetLister.configure() + } catch { + print("Error fetching environment datasets: \(error)") + } + } + } +} + /** TestWorkspaceListView displays the possible workspace datasets whose inputs can be used to simulate the mapping. */ struct TestWorkspaceListView: View { let selectedClasses: [AccessibilityFeatureClass] + let environmentDir: URL + let datasetLister: DatasetLister - @EnvironmentObject var sharedAppData: SharedAppData - @EnvironmentObject var sharedAppContext: SharedAppContext - @EnvironmentObject var userStateViewModel: UserStateViewModel - @EnvironmentObject var workspaceViewModel: WorkspaceViewModel @Environment(\.dismiss) var dismiss - let datasetLister: DatasetLister = DatasetLister() +// let datasetLister: DatasetLister = DatasetLister() var body: some View { VStack { @@ -67,7 +110,8 @@ struct TestWorkspaceListView: View { .navigationBarTitle("Test: Workspace Selection", displayMode: .inline) .onAppear { do { - try datasetLister.configure() +// try datasetLister.configure() + } catch { print("Error fetching workspace datasets: \(error)") } @@ -83,10 +127,6 @@ struct TestChangesetListView: View { let workspaceDir: URL let datasetLister: DatasetLister - @EnvironmentObject var sharedAppData: SharedAppData - @EnvironmentObject var sharedAppContext: SharedAppContext - @EnvironmentObject var userStateViewModel: UserStateViewModel - @EnvironmentObject var workspaceViewModel: WorkspaceViewModel @Environment(\.dismiss) var dismiss var body: some View { From feda821fed2d23d92131a115e7b47456f17773b1 Mon Sep 17 00:00:00 2001 From: himanshunaidu Date: Fri, 29 May 2026 17:28:10 -0700 Subject: [PATCH 2/3] Update DatasetLister to include custom directory structs for easier state management --- .../LocalDataset/DatasetLister.swift | 134 +++++++++++++++--- .../View/TestMode/TestListView.swift | 47 +++--- 2 files changed, 140 insertions(+), 41 deletions(-) diff --git a/IOSAccessAssessment/LocalDataset/DatasetLister.swift b/IOSAccessAssessment/LocalDataset/DatasetLister.swift index d36cc91a..45b627f6 100644 --- a/IOSAccessAssessment/LocalDataset/DatasetLister.swift +++ b/IOSAccessAssessment/LocalDataset/DatasetLister.swift @@ -8,46 +8,136 @@ import Foundation enum DatasetListerError: Error, LocalizedError { + case invalidAPIEnvironment + case directoryRetrievalFailed + case indexDataNotFound(Int) + var errorDescription: String? { + switch self { + case .invalidAPIEnvironment: + return "Invalid API environment." + case .directoryRetrievalFailed: + return "Failed to retrieve dataset directory." + case .indexDataNotFound(let index): + return "Data for index \(index) not found." + } + } +} + +struct EnvironmentDirectory: Identifiable, Comparable, Hashable { + let url: URL + let apiEnvironment: APIEnvironment + + var id: URL { + return url + } + + static func < (lhs: EnvironmentDirectory, rhs: EnvironmentDirectory) -> Bool { + return lhs.url.lastPathComponent < rhs.url.lastPathComponent + } } -class DatasetLister { - var workspaceDirectories: [URL] = [] +struct WorkspaceDirectory: Identifiable, Comparable, Hashable { + let url: URL + let workspaceId: String + + var id: URL { + return url + } + + static func < (lhs: WorkspaceDirectory, rhs: WorkspaceDirectory) -> Bool { + return lhs.url.lastPathComponent < rhs.url.lastPathComponent + } +} + +struct ChangesetDirectory: Identifiable, Comparable, Hashable { + let url: URL + let changesetId: String + + var id: URL { + return url + } - private var workspaceId: String? = nil - private var workspaceDirectory: URL? = nil + static func < (lhs: ChangesetDirectory, rhs: ChangesetDirectory) -> Bool { + return lhs.url.lastPathComponent < rhs.url.lastPathComponent + } +} + +class DatasetLister: ObservableObject { + @Published var environmentDirectories: [EnvironmentDirectory] = [] + @Published var workspaceDirectories: [WorkspaceDirectory] = [] + @Published var changesetDirectories: [ChangesetDirectory] = [] - var changesetDirectories: [URL] = [] + @Published var selectedEnvironment: EnvironmentDirectory? = nil + @Published var selectedWorkspace: WorkspaceDirectory? = nil + @Published var selectedChangeset: ChangesetDirectory? = nil func configure() throws { - self.workspaceDirectories = try DatasetLister.listWorkspaceDirectories() + self.environmentDirectories = try DatasetLister.listEnvironmentDirectories() +// self.workspaceDirectories = try DatasetLister.listWorkspaceDirectories() } - func selectWorkspace(workspaceId: String) throws { - self.workspaceId = workspaceId - /// Get workspace directory - let workspaceDirectory = try self.findDirectory(id: workspaceId) - self.workspaceDirectory = workspaceDirectory - self.changesetDirectories = try self.listChangesetDirectories(workspaceDirectory: workspaceDirectory) + func selectEnvironment(environmentDirectory: EnvironmentDirectory) throws { + self.selectedEnvironment = environmentDirectory + self.workspaceDirectories = try DatasetLister.listWorkspaceDirectories(environmentDirectory: environmentDirectory) } /** - Finds all workspace directories within the app's document directory. + Finds all the environment directories within the app's document directory - A workspace directory is a number-named directory within the document directory, containing data for a specific workspace. - Each workspace directory is expected to contain at least one changeset directory, which is a number-named directory containing data for a specific changeset. + An environment directory is a string-named directory within the document directory, whose string name matches a specific API environment key. + Each environment directory is expected to contain at least one workspace directory, which is a number-named directory containing data for a specific workspace. */ - static func listWorkspaceDirectories() throws -> [URL] { + static func listEnvironmentDirectories() throws -> [EnvironmentDirectory] { guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { - throw DatasetDecoderError.directoryRetrievalFailed + throw DatasetListerError.directoryRetrievalFailed } let contents = try FileManager.default.contentsOfDirectory(at: documentsDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) + var environmentDirectories: [EnvironmentDirectory] = contents.filter { content in + var isDirectory: ObjCBool = false + let exists = FileManager.default.fileExists(atPath: content.path, isDirectory: &isDirectory) + return exists && isDirectory.boolValue && APIEnvironment(rawValue: content.lastPathComponent) != nil + }.compactMap { content in + if let apiEnvironment = APIEnvironment(rawValue: content.lastPathComponent) { + return EnvironmentDirectory(url: content, apiEnvironment: apiEnvironment) + } else { + return nil + } + } + environmentDirectories.sort() + return environmentDirectories + } + + + func selectWorkspace(workspaceDirectory: WorkspaceDirectory) throws { + self.selectedWorkspace = workspaceDirectory + self.changesetDirectories = try self.listChangesetDirectories(workspaceDirectory: workspaceDirectory) + } + +// static func listWorkspaceDirectories() throws -> [URL] { +// guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { +// throw DatasetListerError.directoryRetrievalFailed +// } +// let contents = try FileManager.default.contentsOfDirectory(at: documentsDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) +// var workspaceDirectories = contents.filter { content in +// var isDirectory: ObjCBool = false +// let exists = FileManager.default.fileExists(atPath: content.path, isDirectory: &isDirectory) +// return exists && isDirectory.boolValue && content.lastPathComponent.allSatisfy { $0.isNumber } +// } +// workspaceDirectories = workspaceDirectories.sorted { $0.lastPathComponent < $1.lastPathComponent } +// return workspaceDirectories +// } + static func listWorkspaceDirectories(environmentDirectory: EnvironmentDirectory) throws -> [WorkspaceDirectory] { + let contents = try FileManager.default.contentsOfDirectory(at: environmentDirectory.url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) var workspaceDirectories = contents.filter { content in var isDirectory: ObjCBool = false let exists = FileManager.default.fileExists(atPath: content.path, isDirectory: &isDirectory) return exists && isDirectory.boolValue && content.lastPathComponent.allSatisfy { $0.isNumber } + }.compactMap { content in + let workspaceId = content.lastPathComponent + return WorkspaceDirectory(url: content, workspaceId: workspaceId) } - workspaceDirectories = workspaceDirectories.sorted { $0.lastPathComponent < $1.lastPathComponent } + workspaceDirectories.sort() return workspaceDirectories } @@ -55,13 +145,13 @@ class DatasetLister { var relativeTo = relativeTo if relativeTo == nil { guard let relativeToUrl = FileManager.default.urls(for:.documentDirectory, in: .userDomainMask).first else { - throw DatasetDecoderError.directoryRetrievalFailed + throw DatasetListerError.directoryRetrievalFailed } relativeTo = relativeToUrl } let directory = URL(filePath: id, directoryHint: .isDirectory, relativeTo: relativeTo) guard FileManager.default.fileExists(atPath: directory.path) else { - throw DatasetDecoderError.directoryRetrievalFailed + throw DatasetListerError.directoryRetrievalFailed } return directory } @@ -72,8 +162,8 @@ class DatasetLister { Changesets are number-named directories within the workspace directory, each containing data for a specific changeset. Each changeset directory is expected to contain an rgb directory, within which there is at least one .png file. */ - private func listChangesetDirectories(workspaceDirectory: URL) throws -> [URL] { - let contents = try FileManager.default.contentsOfDirectory(at: workspaceDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) + private func listChangesetDirectories(workspaceDirectory: WorkspaceDirectory) throws -> [URL] { + let contents = try FileManager.default.contentsOfDirectory(at: workspaceDirectory.url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) let changesetDirectories = contents.filter { content in var isDirectory: ObjCBool = false let exists = FileManager.default.fileExists(atPath: content.path, isDirectory: &isDirectory) diff --git a/IOSAccessAssessment/View/TestMode/TestListView.swift b/IOSAccessAssessment/View/TestMode/TestListView.swift index d6aa16ce..bdc47e52 100644 --- a/IOSAccessAssessment/View/TestMode/TestListView.swift +++ b/IOSAccessAssessment/View/TestMode/TestListView.swift @@ -28,7 +28,8 @@ struct TestEnvironmentListView: View { @Environment(\.dismiss) var dismiss - let datasetLister: DatasetLister = DatasetLister() + @StateObject private var datasetLister: DatasetLister = DatasetLister() + @State private var selectedEnvironmentDir: EnvironmentDirectory? var body: some View { VStack { @@ -41,12 +42,10 @@ struct TestEnvironmentListView: View { if datasetLister.environmentDirectories.count > 0 { List { ForEach(datasetLister.environmentDirectories, id: \.self) { environmentDir in - NavigationLink( - destination: TestWorkspaceListView( - selectedClasses: selectedClasses, environmentDir: environmentDir, datasetLister: datasetLister - ) - ) { - Text(environmentDir.lastPathComponent) + Button { + selectEnvironment(environmentDir: environmentDir) + } label: { + Text(environmentDir.url.lastPathComponent) .foregroundColor(.primary) .cornerRadius(8) } @@ -66,6 +65,15 @@ struct TestEnvironmentListView: View { } } } + + func selectEnvironment(environmentDir: EnvironmentDirectory) { + do { + self.selectedEnvironmentDir = environmentDir + try datasetLister.selectEnvironment(environmentDirectory: environmentDir) + } catch { + print("Error selecting environment: \(error)") + } + } } /** @@ -73,8 +81,7 @@ struct TestEnvironmentListView: View { */ struct TestWorkspaceListView: View { let selectedClasses: [AccessibilityFeatureClass] - let environmentDir: URL - let datasetLister: DatasetLister + @ObservedObject var datasetLister: DatasetLister @Environment(\.dismiss) var dismiss @@ -93,10 +100,10 @@ struct TestWorkspaceListView: View { ForEach(datasetLister.workspaceDirectories, id: \.self) { workspaceDir in NavigationLink( destination: TestChangesetListView( - selectedClasses: selectedClasses, workspaceDir: workspaceDir, datasetLister: datasetLister + selectedClasses: selectedClasses, datasetLister: datasetLister ) ) { - Text(workspaceDir.lastPathComponent) + Text(workspaceDir.url.lastPathComponent) .foregroundColor(.primary) .cornerRadius(8) } @@ -111,7 +118,8 @@ struct TestWorkspaceListView: View { .onAppear { do { // try datasetLister.configure() - + let environmentRawValue = environmentDir.lastPathComponent + try datasetLister.selectEnvironment(environmentRawValue: environmentRawValue) } catch { print("Error fetching workspace datasets: \(error)") } @@ -124,8 +132,7 @@ struct TestWorkspaceListView: View { */ struct TestChangesetListView: View { let selectedClasses: [AccessibilityFeatureClass] - let workspaceDir: URL - let datasetLister: DatasetLister + @ObservedObject var datasetLister: DatasetLister @Environment(\.dismiss) var dismiss @@ -140,8 +147,8 @@ struct TestChangesetListView: View { if datasetLister.changesetDirectories.count > 0 { List { ForEach(datasetLister.changesetDirectories, id: \.self) { changesetDir in - NavigationLink(destination: changesetDestination(workspaceDir: workspaceDir, changesetDir: changesetDir)) { - Text(changesetDir.lastPathComponent) + NavigationLink(destination: changesetDestination(changesetDir: changesetDir)) { + Text(changesetDir.url.lastPathComponent) .foregroundColor(.primary) .cornerRadius(8) } @@ -165,10 +172,12 @@ struct TestChangesetListView: View { @ViewBuilder private func changesetDestination(workspaceDir: URL, changesetDir: URL) -> some View { - let workspaceId: String = workspaceDir.lastPathComponent - let changesetId: String = changesetDir.lastPathComponent +// let selectedEnvironment = APIEnvironment(rawValue: environmentDir.lastPathComponent) +// let workspaceId: String = workspaceDir.lastPathComponent +// let changesetId: String = changesetDir.lastPathComponent + let selectedEnvironment = datasetLister.apiEnvironment TestCameraView( - selectedClasses: selectedClasses, + selectedClasses: selectedClasses, selectedEnvironment: selectedEnvironment, workspaceId: workspaceId, changesetId: changesetId ) } From de279c51d84684e87c458da5edf428c68b8b96d7 Mon Sep 17 00:00:00 2001 From: himanshunaidu Date: Fri, 29 May 2026 17:47:17 -0700 Subject: [PATCH 3/3] Complete first version of supporting environment in local dataset creation --- .../LocalDataset/DatasetLister.swift | 15 +++- IOSAccessAssessment/View/SetupView.swift | 2 +- .../View/TestMode/TestListView.swift | 74 +++++++++++-------- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/IOSAccessAssessment/LocalDataset/DatasetLister.swift b/IOSAccessAssessment/LocalDataset/DatasetLister.swift index 45b627f6..c4bc42b4 100644 --- a/IOSAccessAssessment/LocalDataset/DatasetLister.swift +++ b/IOSAccessAssessment/LocalDataset/DatasetLister.swift @@ -162,16 +162,19 @@ class DatasetLister: ObservableObject { Changesets are number-named directories within the workspace directory, each containing data for a specific changeset. Each changeset directory is expected to contain an rgb directory, within which there is at least one .png file. */ - private func listChangesetDirectories(workspaceDirectory: WorkspaceDirectory) throws -> [URL] { + private func listChangesetDirectories(workspaceDirectory: WorkspaceDirectory) throws -> [ChangesetDirectory] { let contents = try FileManager.default.contentsOfDirectory(at: workspaceDirectory.url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) let changesetDirectories = contents.filter { content in var isDirectory: ObjCBool = false let exists = FileManager.default.fileExists(atPath: content.path, isDirectory: &isDirectory) return exists && isDirectory.boolValue && content.lastPathComponent.allSatisfy { $0.isNumber } + }.compactMap { content in + let changesetId = content.lastPathComponent + return ChangesetDirectory(url: content, changesetId: changesetId) } - var finalChangesetDirectories: [URL] = [] + var finalChangesetDirectories: [ChangesetDirectory] = [] for changesetDirectory in changesetDirectories { - let rgbDirectory = changesetDirectory.appending(path: "rgb", directoryHint: .isDirectory) + let rgbDirectory = changesetDirectory.url.appending(path: "rgb", directoryHint: .isDirectory) if FileManager.default.fileExists(atPath: rgbDirectory.path) { let pngFiles = try FileManager.default.contentsOfDirectory(at: rgbDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]).filter { $0.pathExtension.lowercased() == "png" } if !pngFiles.isEmpty { @@ -179,7 +182,11 @@ class DatasetLister: ObservableObject { } } } - finalChangesetDirectories = finalChangesetDirectories.sorted { $0.lastPathComponent < $1.lastPathComponent } + finalChangesetDirectories.sort() return finalChangesetDirectories } + + func selectChangeset(changesetDirectory: ChangesetDirectory) { + self.selectedChangeset = changesetDirectory + } } diff --git a/IOSAccessAssessment/View/SetupView.swift b/IOSAccessAssessment/View/SetupView.swift index ff84a458..cee2e433 100644 --- a/IOSAccessAssessment/View/SetupView.swift +++ b/IOSAccessAssessment/View/SetupView.swift @@ -450,7 +450,7 @@ struct SetupView: View { if userStateViewModel.appMode == .standard { ARCameraView(selectedClasses: Array(self.selectedClasses).sorted()) } else { - TestWorkspaceListView(selectedClasses: Array(self.selectedClasses).sorted()) + TestEnvironmentListView(selectedClasses: Array(self.selectedClasses).sorted()) } } diff --git a/IOSAccessAssessment/View/TestMode/TestListView.swift b/IOSAccessAssessment/View/TestMode/TestListView.swift index bdc47e52..a439310a 100644 --- a/IOSAccessAssessment/View/TestMode/TestListView.swift +++ b/IOSAccessAssessment/View/TestMode/TestListView.swift @@ -29,7 +29,7 @@ struct TestEnvironmentListView: View { @Environment(\.dismiss) var dismiss @StateObject private var datasetLister: DatasetLister = DatasetLister() - @State private var selectedEnvironmentDir: EnvironmentDirectory? + @State private var selectedEnvironment: APIEnvironment? var body: some View { VStack { @@ -64,12 +64,15 @@ struct TestEnvironmentListView: View { print("Error fetching environment datasets: \(error)") } } + .navigationDestination(item: $selectedEnvironment) { environmentDir in + TestWorkspaceListView(selectedClasses: selectedClasses, datasetLister: datasetLister) + } } func selectEnvironment(environmentDir: EnvironmentDirectory) { do { - self.selectedEnvironmentDir = environmentDir try datasetLister.selectEnvironment(environmentDirectory: environmentDir) + self.selectedEnvironment = environmentDir.apiEnvironment } catch { print("Error selecting environment: \(error)") } @@ -85,6 +88,7 @@ struct TestWorkspaceListView: View { @Environment(\.dismiss) var dismiss + @State private var selectedWorkspace: WorkspaceDirectory? // let datasetLister: DatasetLister = DatasetLister() var body: some View { @@ -98,11 +102,9 @@ struct TestWorkspaceListView: View { if datasetLister.workspaceDirectories.count > 0 { List { ForEach(datasetLister.workspaceDirectories, id: \.self) { workspaceDir in - NavigationLink( - destination: TestChangesetListView( - selectedClasses: selectedClasses, datasetLister: datasetLister - ) - ) { + Button { + selectWorkspace(workspaceDir: workspaceDir) + } label: { Text(workspaceDir.url.lastPathComponent) .foregroundColor(.primary) .cornerRadius(8) @@ -115,14 +117,17 @@ struct TestWorkspaceListView: View { } } .navigationBarTitle("Test: Workspace Selection", displayMode: .inline) - .onAppear { - do { -// try datasetLister.configure() - let environmentRawValue = environmentDir.lastPathComponent - try datasetLister.selectEnvironment(environmentRawValue: environmentRawValue) - } catch { - print("Error fetching workspace datasets: \(error)") - } + .navigationDestination(item: $selectedWorkspace) { workspaceDir in + TestChangesetListView(selectedClasses: selectedClasses, datasetLister: datasetLister) + } + } + + func selectWorkspace(workspaceDir: WorkspaceDirectory) { + do { + try datasetLister.selectWorkspace(workspaceDirectory: workspaceDir) + self.selectedWorkspace = workspaceDir + } catch { + print("Error selecting workspace: \(error)") } } } @@ -136,6 +141,8 @@ struct TestChangesetListView: View { @Environment(\.dismiss) var dismiss + @State private var selectedChangeset: ChangesetDirectory? + var body: some View { VStack { HStack { @@ -147,7 +154,9 @@ struct TestChangesetListView: View { if datasetLister.changesetDirectories.count > 0 { List { ForEach(datasetLister.changesetDirectories, id: \.self) { changesetDir in - NavigationLink(destination: changesetDestination(changesetDir: changesetDir)) { + Button { + selectChangeset(changesetDir: changesetDir) + } label: { Text(changesetDir.url.lastPathComponent) .foregroundColor(.primary) .cornerRadius(8) @@ -160,25 +169,26 @@ struct TestChangesetListView: View { } } .navigationBarTitle("Test: Changeset Selection", displayMode: .inline) - .onAppear { - do { - let workspaceId = workspaceDir.lastPathComponent - try datasetLister.selectWorkspace(workspaceId: workspaceId) - } catch { - print("Error selecting workspace: \(error)") - } + .navigationDestination(item: $selectedChangeset) { changesetDir in + changesetDestination(changesetDir: changesetDir) } } + func selectChangeset(changesetDir: ChangesetDirectory) { + self.selectedChangeset = changesetDir + datasetLister.selectChangeset(changesetDirectory: changesetDir) + } + @ViewBuilder - private func changesetDestination(workspaceDir: URL, changesetDir: URL) -> some View { -// let selectedEnvironment = APIEnvironment(rawValue: environmentDir.lastPathComponent) -// let workspaceId: String = workspaceDir.lastPathComponent -// let changesetId: String = changesetDir.lastPathComponent - let selectedEnvironment = datasetLister.apiEnvironment - TestCameraView( - selectedClasses: selectedClasses, selectedEnvironment: selectedEnvironment, - workspaceId: workspaceId, changesetId: changesetId - ) + private func changesetDestination(changesetDir: ChangesetDirectory) -> some View { + if let selectedEnvironment = datasetLister.selectedEnvironment, + let selectedWorkspace = datasetLister.selectedWorkspace { + TestCameraView( + selectedClasses: selectedClasses, selectedEnvironment: selectedEnvironment.apiEnvironment, + workspaceId: selectedWorkspace.workspaceId, changesetId: changesetDir.changesetId + ) + } else { + Text("Missing environment or workspace selection.") + } } }