diff --git a/LabWise.xcodeproj/project.pbxproj b/LabWise.xcodeproj/project.pbxproj index 67ff70e..ca868d2 100644 --- a/LabWise.xcodeproj/project.pbxproj +++ b/LabWise.xcodeproj/project.pbxproj @@ -400,11 +400,14 @@ DEVELOPMENT_TEAM = YK2DB9NT3S; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; + INFOPLIST_KEY_NSCameraUsageDescription = "Camera Access needed for scanning chemical labels for adding to inventory."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -432,11 +435,14 @@ DEVELOPMENT_TEAM = YK2DB9NT3S; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; + INFOPLIST_KEY_NSCameraUsageDescription = "Camera Access needed for scanning chemical labels for adding to inventory."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/LabWise/Assets.xcassets/BrandDestructive.colorset/Contents.json b/LabWise/Assets.xcassets/BrandDestructive.colorset/Contents.json new file mode 100644 index 0000000..dd52cab --- /dev/null +++ b/LabWise/Assets.xcassets/BrandDestructive.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3D", + "green" : "0x18", + "red" : "0xD4" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LabWise/Assets.xcassets/BrandMuted.colorset/Contents.json b/LabWise/Assets.xcassets/BrandMuted.colorset/Contents.json new file mode 100644 index 0000000..5022054 --- /dev/null +++ b/LabWise/Assets.xcassets/BrandMuted.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF3", + "green" : "0xF5", + "red" : "0xF0" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LabWise/Assets.xcassets/BrandMutedForeground.colorset/Contents.json b/LabWise/Assets.xcassets/BrandMutedForeground.colorset/Contents.json new file mode 100644 index 0000000..975b724 --- /dev/null +++ b/LabWise/Assets.xcassets/BrandMutedForeground.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x6F", + "green" : "0x7A", + "red" : "0x5A" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LabWise/Assets.xcassets/BrandPrimary.colorset/Contents.json b/LabWise/Assets.xcassets/BrandPrimary.colorset/Contents.json new file mode 100644 index 0000000..7409fad --- /dev/null +++ b/LabWise/Assets.xcassets/BrandPrimary.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x4A", + "green" : "0x5A", + "red" : "0x2D" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LabWise/Assets.xcassets/BrandSecondary.colorset/Contents.json b/LabWise/Assets.xcassets/BrandSecondary.colorset/Contents.json new file mode 100644 index 0000000..70b9aad --- /dev/null +++ b/LabWise/Assets.xcassets/BrandSecondary.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF0", + "green" : "0xF3", + "red" : "0xE8" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LabWise/Assets.xcassets/GoogleLogo.imageset/Contents.json b/LabWise/Assets.xcassets/GoogleLogo.imageset/Contents.json new file mode 100644 index 0000000..c6508ba --- /dev/null +++ b/LabWise/Assets.xcassets/GoogleLogo.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "google-logo.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/LabWise/Assets.xcassets/GoogleLogo.imageset/google-logo.svg b/LabWise/Assets.xcassets/GoogleLogo.imageset/google-logo.svg new file mode 100644 index 0000000..a771b22 --- /dev/null +++ b/LabWise/Assets.xcassets/GoogleLogo.imageset/google-logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/LabWise/Assets.xcassets/Logo.imageset/Contents.json b/LabWise/Assets.xcassets/Logo.imageset/Contents.json new file mode 100644 index 0000000..5f670ca --- /dev/null +++ b/LabWise/Assets.xcassets/Logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LabWise/Assets.xcassets/Logo.imageset/logo.png b/LabWise/Assets.xcassets/Logo.imageset/logo.png new file mode 100644 index 0000000..a6b0969 Binary files /dev/null and b/LabWise/Assets.xcassets/Logo.imageset/logo.png differ diff --git a/LabWise/ContentView.swift b/LabWise/ContentView.swift deleted file mode 100644 index 8dca6af..0000000 --- a/LabWise/ContentView.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ContentView.swift -// LabWise -// -// Created by Aditya Pulipaka on 3/19/26. -// - -import SwiftUI - -struct ContentView: View { - var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") - } - .padding() - } -} - -#Preview { - ContentView() -} diff --git a/LabWise/DashboardView.swift b/LabWise/DashboardView.swift new file mode 100644 index 0000000..11641bd --- /dev/null +++ b/LabWise/DashboardView.swift @@ -0,0 +1,22 @@ +import SwiftUI + +struct DashboardView: View { + var body: some View { + TabView { + Tab("Chemicals", systemImage: "flask.fill") { + ChemicalsListView() + } + Tab("Scan", systemImage: "camera.fill") { + ScanView() + } + Tab("Profile", systemImage: "person.fill") { + ProfileView() + } + } + .tint(Color("Brand/BrandPrimary")) + } +} + +#Preview { + DashboardView() +} diff --git a/LabWise/LoginView.swift b/LabWise/LoginView.swift new file mode 100644 index 0000000..b800b36 --- /dev/null +++ b/LabWise/LoginView.swift @@ -0,0 +1,200 @@ +import SwiftUI +import AuthenticationServices +import LabWiseKit + +struct LoginView: View { + @Environment(AppState.self) private var appState + + @State private var email = "" + @State private var password = "" + @State private var isLoading = false + @State private var isGoogleLoading = false + @State private var errorMessage: String? + + private let authClient = AuthClient() + + var body: some View { + NavigationStack { + ZStack { + Color(UIColor.systemGroupedBackground) + .ignoresSafeArea() + + VStack(spacing: 32) { + Spacer() + + // Logo + VStack(spacing: 16) { + Image("Logo") + .resizable() + .scaledToFit() + .frame(height: 52) + Text("Chemical Inventory Management") + .font(.subheadline) + .foregroundStyle(Color("Brand/BrandMutedForeground")) + } + + // Card + VStack(spacing: 20) { + VStack(spacing: 12) { + TextField("Email", text: $email) + .keyboardType(.emailAddress) + .textContentType(.emailAddress) + .autocapitalization(.none) + .padding(.horizontal, 12) + .padding(.vertical, 10) + .background(Color(UIColor.secondarySystemGroupedBackground)) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .overlay( + RoundedRectangle(cornerRadius: 10) + .strokeBorder(Color("Brand/BrandPrimary").opacity(0.2), lineWidth: 1) + ) + + SecureField("Password", text: $password) + .textContentType(.password) + .padding(.horizontal, 12) + .padding(.vertical, 10) + .background(Color(UIColor.secondarySystemGroupedBackground)) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .overlay( + RoundedRectangle(cornerRadius: 10) + .strokeBorder(Color("Brand/BrandPrimary").opacity(0.2), lineWidth: 1) + ) + } + + if let errorMessage { + Text(errorMessage) + .foregroundStyle(.red) + .font(.footnote) + .multilineTextAlignment(.center) + } + + Button { + Task { await signIn() } + } label: { + Group { + if isLoading { + ProgressView() + .tint(.white) + } else { + Text("Sign In") + .fontWeight(.semibold) + } + } + .frame(maxWidth: .infinity) + .frame(height: 44) + } + .background(Color("Brand/BrandPrimary")) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .disabled(email.isEmpty || password.isEmpty || isLoading || isGoogleLoading) + .opacity((email.isEmpty || password.isEmpty || isLoading || isGoogleLoading) ? 0.5 : 1) + + HStack { + Rectangle() + .fill(Color("Brand/BrandPrimary").opacity(0.15)) + .frame(height: 1) + Text("or") + .font(.footnote) + .foregroundStyle(Color("Brand/BrandMutedForeground")) + Rectangle() + .fill(Color("Brand/BrandPrimary").opacity(0.15)) + .frame(height: 1) + } + + GoogleSignInButton(isLoading: isGoogleLoading) { + Task { await signInWithGoogle() } + } + .disabled(isLoading || isGoogleLoading) + } + .padding(24) + .background(Color(UIColor.secondarySystemGroupedBackground)) + .clipShape(RoundedRectangle(cornerRadius: 16)) + .shadow(color: Color("Brand/BrandPrimary").opacity(0.06), radius: 12, x: 0, y: 4) + .padding(.horizontal, 24) + + Spacer() + } + } + .navigationBarHidden(true) + } + } + + // MARK: - Actions + + private func signIn() async { + isLoading = true + errorMessage = nil + defer { isLoading = false } + + do { + let user = try await authClient.signIn(email: email, password: password) + await MainActor.run { appState.signedIn(user: user) } + } catch APIError.httpError(let code, _) where code == 401 { + await MainActor.run { errorMessage = "Invalid email or password." } + } catch APIError.httpError(_, let data) { + let msg = String(data: data, encoding: .utf8) ?? "Sign in failed." + await MainActor.run { errorMessage = msg } + } catch { + await MainActor.run { errorMessage = "Sign in failed. Please check your connection." } + } + } + + @MainActor + private func signInWithGoogle() async { + guard let window = UIApplication.shared.connectedScenes + .compactMap({ $0 as? UIWindowScene }) + .flatMap({ $0.windows }) + .first(where: { $0.isKeyWindow }) else { return } + + isGoogleLoading = true + errorMessage = nil + defer { isGoogleLoading = false } + + do { + let user = try await authClient.signInWithGoogle(presentingWindow: window) + appState.signedIn(user: user) + } catch ASWebAuthenticationSessionError.canceledLogin { + // User cancelled — no error shown + } catch { + errorMessage = "Google sign-in failed. Please try again." + } + } +} + +// MARK: - Google button + +struct GoogleSignInButton: View { + let isLoading: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + HStack(spacing: 10) { + if isLoading { + ProgressView() + .frame(maxWidth: .infinity) + } else { + Image(systemName: "globe") + .foregroundStyle(Color("Brand/BrandPrimary")) + Text("Continue with Google") + .fontWeight(.medium) + .foregroundStyle(Color(UIColor.label)) + } + } + .frame(maxWidth: .infinity) + .frame(height: 44) + .background(Color(UIColor.systemBackground)) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .overlay( + RoundedRectangle(cornerRadius: 10) + .strokeBorder(Color("Brand/BrandPrimary").opacity(0.25), lineWidth: 1) + ) + } + .buttonStyle(.plain) + } +} + +#Preview { + LoginView() + .environment(AppState.shared) +} diff --git a/LabWiseKit/.gitignore b/LabWiseKit/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/LabWiseKit/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/LabWiseKit/Package.swift b/LabWiseKit/Package.swift new file mode 100644 index 0000000..4b7928f --- /dev/null +++ b/LabWiseKit/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "LabWiseKit", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "LabWiseKit", + targets: ["LabWiseKit"] + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "LabWiseKit" + ), + .testTarget( + name: "LabWiseKitTests", + dependencies: ["LabWiseKit"] + ), + ] +) diff --git a/LabWiseKit/Sources/LabWiseKit/LabWiseKit.swift b/LabWiseKit/Sources/LabWiseKit/LabWiseKit.swift new file mode 100644 index 0000000..08b22b8 --- /dev/null +++ b/LabWiseKit/Sources/LabWiseKit/LabWiseKit.swift @@ -0,0 +1,2 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book diff --git a/LabWiseKit/Sources/LabWiseKit/Models/Chemical.swift b/LabWiseKit/Sources/LabWiseKit/Models/Chemical.swift new file mode 100644 index 0000000..f239168 --- /dev/null +++ b/LabWiseKit/Sources/LabWiseKit/Models/Chemical.swift @@ -0,0 +1,164 @@ +import Foundation + +public struct Chemical: Codable, Identifiable, Sendable { + public let id: String + // Required + public var piFirstName: String + public var physicalState: String + public var chemicalName: String + public var bldgCode: String + public var lab: String + public var storageLocation: String + public var storageDevice: String + public var numberOfContainers: String + public var amountPerContainer: String + public var unitOfMeasure: String + public var casNumber: String + // Optional + public var chemicalFormula: String? + public var molecularWeight: String? + public var vendor: String? + public var catalogNumber: String? + public var lotNumber: String? + public var expirationDate: String? + public var concentration: String? + public var percentageFull: Double? + public var comments: String? + public var barcode: String? + public var contact: String? + public var scannedImage: String? + public var needsManualEntry: [String]? + public var createdAt: String? + public var updatedAt: String? + + public init( + id: String = "", + piFirstName: String = "", + physicalState: String = "", + chemicalName: String = "", + bldgCode: String = "", + lab: String = "", + storageLocation: String = "", + storageDevice: String = "", + numberOfContainers: String = "", + amountPerContainer: String = "", + unitOfMeasure: String = "", + casNumber: String = "", + chemicalFormula: String? = nil, + molecularWeight: String? = nil, + vendor: String? = nil, + catalogNumber: String? = nil, + lotNumber: String? = nil, + expirationDate: String? = nil, + concentration: String? = nil, + percentageFull: Double? = nil, + comments: String? = nil, + barcode: String? = nil, + contact: String? = nil, + scannedImage: String? = nil, + needsManualEntry: [String]? = nil, + createdAt: String? = nil, + updatedAt: String? = nil + ) { + self.id = id + self.piFirstName = piFirstName + self.physicalState = physicalState + self.chemicalName = chemicalName + self.bldgCode = bldgCode + self.lab = lab + self.storageLocation = storageLocation + self.storageDevice = storageDevice + self.numberOfContainers = numberOfContainers + self.amountPerContainer = amountPerContainer + self.unitOfMeasure = unitOfMeasure + self.casNumber = casNumber + self.chemicalFormula = chemicalFormula + self.molecularWeight = molecularWeight + self.vendor = vendor + self.catalogNumber = catalogNumber + self.lotNumber = lotNumber + self.expirationDate = expirationDate + self.concentration = concentration + self.percentageFull = percentageFull + self.comments = comments + self.barcode = barcode + self.contact = contact + self.scannedImage = scannedImage + self.needsManualEntry = needsManualEntry + self.createdAt = createdAt + self.updatedAt = updatedAt + } +} + +public struct ChemicalCreateBody: Codable, Sendable { + public var piFirstName: String + public var physicalState: String + public var chemicalName: String + public var bldgCode: String + public var lab: String + public var storageLocation: String + public var storageDevice: String + public var numberOfContainers: String + public var amountPerContainer: String + public var unitOfMeasure: String + public var casNumber: String + public var chemicalFormula: String? + public var molecularWeight: String? + public var vendor: String? + public var catalogNumber: String? + public var lotNumber: String? + public var expirationDate: String? + public var concentration: String? + public var percentageFull: Double? + public var comments: String? + public var barcode: String? + public var contact: String? + + public init( + piFirstName: String, + physicalState: String, + chemicalName: String, + bldgCode: String, + lab: String, + storageLocation: String, + storageDevice: String, + numberOfContainers: String, + amountPerContainer: String, + unitOfMeasure: String, + casNumber: String, + chemicalFormula: String? = nil, + molecularWeight: String? = nil, + vendor: String? = nil, + catalogNumber: String? = nil, + lotNumber: String? = nil, + expirationDate: String? = nil, + concentration: String? = nil, + percentageFull: Double? = nil, + comments: String? = nil, + barcode: String? = nil, + contact: String? = nil + ) { + self.piFirstName = piFirstName + self.physicalState = physicalState + self.chemicalName = chemicalName + self.bldgCode = bldgCode + self.lab = lab + self.storageLocation = storageLocation + self.storageDevice = storageDevice + self.numberOfContainers = numberOfContainers + self.amountPerContainer = amountPerContainer + self.unitOfMeasure = unitOfMeasure + self.casNumber = casNumber + self.chemicalFormula = chemicalFormula + self.molecularWeight = molecularWeight + self.vendor = vendor + self.catalogNumber = catalogNumber + self.lotNumber = lotNumber + self.expirationDate = expirationDate + self.concentration = concentration + self.percentageFull = percentageFull + self.comments = comments + self.barcode = barcode + self.contact = contact + } +} diff --git a/LabWiseKit/Sources/LabWiseKit/Models/LabProtocol.swift b/LabWiseKit/Sources/LabWiseKit/Models/LabProtocol.swift new file mode 100644 index 0000000..fb166e2 --- /dev/null +++ b/LabWiseKit/Sources/LabWiseKit/Models/LabProtocol.swift @@ -0,0 +1,83 @@ +import Foundation + +/// A lab protocol document. Named `LabProtocol` to avoid collision with the Swift keyword `Protocol`. +public struct LabProtocol: Codable, Identifiable, Sendable { + public let id: String + public var title: String + public var content: String + public var fileUrl: String? + public var analysisResults: AnyCodable? + public var createdAt: String? + public var updatedAt: String? + + enum CodingKeys: String, CodingKey { + case id, title, content, fileUrl, analysisResults, createdAt, updatedAt + } + + public init( + id: String = "", + title: String = "", + content: String = "", + fileUrl: String? = nil, + analysisResults: AnyCodable? = nil, + createdAt: String? = nil, + updatedAt: String? = nil + ) { + self.id = id + self.title = title + self.content = content + self.fileUrl = fileUrl + self.analysisResults = analysisResults + self.createdAt = createdAt + self.updatedAt = updatedAt + } +} + +// MARK: - AnyCodable helper + +public struct AnyCodable: Codable, Sendable { + public let value: any Sendable + + public init(_ value: any Sendable) { + self.value = value + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if let intVal = try? container.decode(Int.self) { + value = intVal + } else if let doubleVal = try? container.decode(Double.self) { + value = doubleVal + } else if let boolVal = try? container.decode(Bool.self) { + value = boolVal + } else if let stringVal = try? container.decode(String.self) { + value = stringVal + } else if let arrayVal = try? container.decode([AnyCodable].self) { + value = arrayVal + } else if let dictVal = try? container.decode([String: AnyCodable].self) { + value = dictVal + } else { + value = NSNull() + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch value { + case let intVal as Int: + try container.encode(intVal) + case let doubleVal as Double: + try container.encode(doubleVal) + case let boolVal as Bool: + try container.encode(boolVal) + case let stringVal as String: + try container.encode(stringVal) + case let arrayVal as [AnyCodable]: + try container.encode(arrayVal) + case let dictVal as [String: AnyCodable]: + try container.encode(dictVal) + default: + try container.encodeNil() + } + } +} diff --git a/LabWiseKit/Tests/LabWiseKitTests/LabWiseKitTests.swift b/LabWiseKit/Tests/LabWiseKitTests/LabWiseKitTests.swift new file mode 100644 index 0000000..3e960a2 --- /dev/null +++ b/LabWiseKit/Tests/LabWiseKitTests/LabWiseKitTests.swift @@ -0,0 +1,6 @@ +import Testing +@testable import LabWiseKit + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/LabWiseUITests/LabWiseKit/.gitignore b/LabWiseUITests/LabWiseKit/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/LabWiseUITests/LabWiseKit/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/LabWiseUITests/LabWiseKit/Package.swift b/LabWiseUITests/LabWiseKit/Package.swift new file mode 100644 index 0000000..4b7928f --- /dev/null +++ b/LabWiseUITests/LabWiseKit/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "LabWiseKit", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "LabWiseKit", + targets: ["LabWiseKit"] + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "LabWiseKit" + ), + .testTarget( + name: "LabWiseKitTests", + dependencies: ["LabWiseKit"] + ), + ] +) diff --git a/LabWiseUITests/LabWiseKit/Sources/LabWiseKit/LabWiseKit.swift b/LabWiseUITests/LabWiseKit/Sources/LabWiseKit/LabWiseKit.swift new file mode 100644 index 0000000..08b22b8 --- /dev/null +++ b/LabWiseUITests/LabWiseKit/Sources/LabWiseKit/LabWiseKit.swift @@ -0,0 +1,2 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book diff --git a/LabWiseUITests/LabWiseKit/Tests/LabWiseKitTests/LabWiseKitTests.swift b/LabWiseUITests/LabWiseKit/Tests/LabWiseKitTests/LabWiseKitTests.swift new file mode 100644 index 0000000..3e960a2 --- /dev/null +++ b/LabWiseUITests/LabWiseKit/Tests/LabWiseKitTests/LabWiseKitTests.swift @@ -0,0 +1,6 @@ +import Testing +@testable import LabWiseKit + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +}