SubmittedToTestflight

This commit is contained in:
2026-03-20 03:39:17 -05:00
parent 813031c823
commit 2ee3f37576
5 changed files with 60 additions and 56 deletions

View File

@@ -433,6 +433,7 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LabWise/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LabWise;
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;
@@ -446,7 +447,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.adipu.LabWise;
PRODUCT_BUNDLE_IDENTIFIER = com.adipu.LabWiseApp;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
@@ -469,6 +470,7 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LabWise/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LabWise;
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;
@@ -482,7 +484,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.adipu.LabWise;
PRODUCT_BUNDLE_IDENTIFIER = com.adipu.LabWiseApp;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;

View File

@@ -1,6 +1,7 @@
{
"images" : [
{
"filename" : "LabWiseLogo1.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

View File

@@ -11,27 +11,17 @@ public struct SignInBody: Encodable, Sendable {
}
}
/// Better Auth wraps the sign-in response as { data: { user, session } }
/// Server returns { token, user } directly (or { error: { message } } on failure)
public struct SignInResponse: Decodable, Sendable {
public let data: SignInData?
public let token: String?
public let user: AuthUser?
public let error: SignInError?
public struct SignInData: Decodable, Sendable {
public let user: AuthUser
public let session: AuthSession
}
public struct SignInError: Decodable, Sendable {
public let message: String?
}
}
public struct AuthSession: Decodable, Sendable {
public let id: String
public let token: String
public let userId: String
public let expiresAt: String
}
public struct AuthUser: Decodable, Sendable {
public let id: String
@@ -41,10 +31,9 @@ public struct AuthUser: Decodable, Sendable {
public let image: String?
}
/// Thin wrapper around the /api/auth/session endpoint response.
/// Thin wrapper around the /api/auth/get-session endpoint response.
private struct SessionResponse: Decodable, Sendable {
let user: AuthUser?
let session: AuthSession?
}
public final class AuthClient: Sendable {
@@ -64,6 +53,7 @@ public final class AuthClient: Sendable {
var req = URLRequest(url: URL(string: "\(baseURL)/api/auth/sign-in/email")!)
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.setValue(baseURL, forHTTPHeaderField: "Origin")
req.httpBody = encoded
let session = sharedURLSession()
@@ -73,19 +63,22 @@ public final class AuthClient: Sendable {
throw APIError.networkError(URLError(.badServerResponse))
}
// Better Auth may return 200 with an error body, or a non-200 status
guard http.statusCode == 200 else {
throw APIError.httpError(statusCode: http.statusCode, data: data)
}
let signInResponse = try JSONDecoder.api.decode(SignInResponse.self, from: data)
if let errMsg = signInResponse.error?.message {
throw APIError.httpError(statusCode: http.statusCode, data: Data(errMsg.utf8))
}
guard http.statusCode == 200 else {
throw APIError.httpError(statusCode: http.statusCode, data: data)
}
guard let user = signInResponse.data?.user else {
guard let user = signInResponse.user else {
throw APIError.decodingError(DecodingError.dataCorrupted(
.init(codingPath: [], debugDescription: "Missing user in sign-in response")
))
}
if let token = signInResponse.token {
injectSessionCookie(token: token)
}
return user
}
@@ -194,6 +187,7 @@ public final class AuthClient: Sendable {
public func fetchCurrentUser() async throws -> AuthUser {
var req = URLRequest(url: URL(string: "\(baseURL)/api/auth/get-session")!)
req.httpMethod = "GET"
req.setValue(baseURL, forHTTPHeaderField: "Origin")
let session = sharedURLSession()
let (data, response) = try await session.data(for: req)
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {

View File

@@ -29,15 +29,6 @@ public struct Chemical: Identifiable, Sendable, Encodable {
public var createdAt: String?
public var updatedAt: String?
enum CodingKeys: String, CodingKey {
case id, piFirstName, physicalState, chemicalName, bldgCode, lab
case storageLocation, storageDevice, numberOfContainers, amountPerContainer
case unitOfMeasure, casNumber, chemicalFormula, molecularWeight, vendor
case catalogNumber, lotNumber, expirationDate, concentration, percentageFull
case comments, barcode, contact, scannedImage, needsManualEntry
case createdAt, updatedAt
}
public init(
id: String = "",
piFirstName: String = "",
@@ -117,7 +108,6 @@ extension Chemical: Decodable {
vendor = try c.decodeIfPresent(String.self, forKey: .vendor)
catalogNumber = try c.decodeIfPresent(String.self, forKey: .catalogNumber)
lotNumber = try c.decodeIfPresent(String.self, forKey: .lotNumber)
expirationDate = try c.decodeIfPresent(String.self, forKey: .expirationDate)
concentration = try c.decodeIfPresent(String.self, forKey: .concentration)
comments = try c.decodeIfPresent(String.self, forKey: .comments)
barcode = try c.decodeIfPresent(String.self, forKey: .barcode)
@@ -127,7 +117,7 @@ extension Chemical: Decodable {
createdAt = try c.decodeIfPresent(String.self, forKey: .createdAt)
updatedAt = try c.decodeIfPresent(String.self, forKey: .updatedAt)
// percentageFull arrives as a number OR a numeric string ("12.00") depending on the query path
// percentageFull: GET returns Float (after ::float cast), POST/PATCH RETURNING still returns String
if let d = try? c.decodeIfPresent(Double.self, forKey: .percentageFull) {
percentageFull = d
} else if let s = try? c.decodeIfPresent(String.self, forKey: .percentageFull) {
@@ -135,6 +125,23 @@ extension Chemical: Decodable {
} else {
percentageFull = nil
}
// expirationDate: GET returns "YYYY-MM-DD", POST/PATCH RETURNING returns full ISO timestamp
// Normalise to "YYYY-MM-DD" in all cases
if let raw = try? c.decodeIfPresent(String.self, forKey: .expirationDate) {
expirationDate = String(raw.prefix(10))
} else {
expirationDate = nil
}
}
enum CodingKeys: String, CodingKey {
case id, piFirstName, physicalState, chemicalName, bldgCode, lab
case storageLocation, storageDevice, numberOfContainers, amountPerContainer
case unitOfMeasure, casNumber, chemicalFormula, molecularWeight, vendor
case catalogNumber, lotNumber, expirationDate, concentration, percentageFull
case comments, barcode, contact, scannedImage, needsManualEntry
case createdAt, updatedAt
}
}