6 Commits
Author SHA1 Message Date
Puranjay Savar Mattas 4faa23f79e chore(xcode): sync project signing config after bundle ID rename
Picks up Xcode's own automatic-signing additions from setting up the
Development provisioning profile in the IDE (CODE_SIGN_IDENTITY,
PROVISIONING_PROFILE_SPECIFIER, ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS,
Info.plist display name/category keys), alongside the earlier
com.psmattas.OutpostApp bundle ID and CW6GQT9SK5 team fixes.
2026-08-18 13:52:32 +01:00
Puranjay Savar Mattas 09e1ab8f65 fix(auth): properly clean the login logo edge, previous pass left a fringe
Plain -fuzz -transparent white color-keyed every pixel in the whole
image close enough to white by color distance — including an
antialiased blend inside the artwork itself (where the light beam meets
the window frame), which punched a stray transparent hole there, and
still left a faint opaque fringe ring around the outer edge since the
binary cutoff didn't fully consume the antialiasing ramp.

Redone with a flood fill seeded from a corner pixel instead: only
transforms the background region actually connected to that seed (the
four corners + thin white border, confirmed contiguous), leaving
disconnected internal antialiased pixels untouched. Then eroded the
alpha channel by ~2px to consume the remaining boundary ramp cleanly.
Verified against both dark and light composited backgrounds, and
confirmed the beam/window-frame area is no longer punctured.
2026-08-18 12:39:42 +01:00
Puranjay Savar Mattas bec8135c74 fix(auth): remove white corners around the login logo
The App Store marketing icon source (outpost-ios-1024.png) is a flat
opaque square with the squircle baked in and pure-white corners outside
it — meant to be masked by the OS everywhere it's normally shown as an
app icon, but nothing masks it when used as a plain in-app image.
Color-keyed white to transparent (fuzz 8%, safe here since nothing in
the actual artwork is white/near-white) so the corners disappear instead
of showing a white border.
2026-08-18 12:30:05 +01:00
Puranjay Savar Mattas fcb72ba9d6 fix(auth): login logo was blank, AppIcon app-icon set isn't Image()-loadable
Image("AppIcon") resolved to nothing at runtime (confirmed live) — App
Icon-type asset catalog entries aren't reliably retrievable through
Image(_:)/UIImage(named:) the way a normal Image Set is. Added AppLogo,
a plain Image Set duplicating the same outpost-ios-1024.png artwork, and
pointed AuthHeaderView at that instead.
2026-08-18 12:22:16 +01:00
Puranjay Savar Mattas 2b0924be04 fix(auth): use the real app icon on the login screen, not a placeholder
AuthHeaderView was still a generic gradient-circle + SF Symbol book icon
from before AppIcon.appiconset had real branded artwork — never swapped
out once the actual icon was added. Now renders Image("AppIcon") directly
so the login screen can't drift from whatever the Dock/Home Screen icon
actually is.
2026-08-18 12:09:52 +01:00
Puranjay Savar Mattas 931338c9d3 fix(session): don't show signed-in UI when only half the session survived
SessionStore.init treated a Keychain token alone as "signed in," without
checking the paired serverURL in UserDefaults also existed. UserDefaults
is scoped to the app's sandboxed container (keyed by bundle ID), while
Keychain items can survive a reinstall or bundle ID change independently
of it — hit this live after renaming the bundle ID: Keychain still had
the old token, the new container had no serverURL, so isSignedIn came
back true with apiClient nil. App landed on Home with nothing able to
load instead of the login screen.

Now requires both to be present to consider the session valid, and clears
whichever half survived otherwise so a fresh sign-in rewrites both
consistently.
2026-08-18 12:07:02 +01:00
5 changed files with 73 additions and 28 deletions
+22 -10
View File
@@ -299,7 +299,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -361,7 +361,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -385,15 +385,20 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = Outpost/Outpost.entitlements; CODE_SIGN_ENTITLEMENTS = Outpost/Outpost.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
ENABLE_APP_SANDBOX = YES; ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
ENABLE_USER_SELECTED_FILES = readonly; ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Outpost;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
@@ -409,8 +414,9 @@
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 27.0; MACOSX_DEPLOYMENT_TARGET = 27.0;
MARKETING_VERSION = 0.0.3; MARKETING_VERSION = 0.0.3;
PRODUCT_BUNDLE_IDENTIFIER = com.psmattas.Outpost; PRODUCT_BUNDLE_IDENTIFIER = com.psmattas.OutpostApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
REGISTER_APP_GROUPS = YES; REGISTER_APP_GROUPS = YES;
SDKROOT = auto; SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -430,15 +436,20 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = Outpost/Outpost.entitlements; CODE_SIGN_ENTITLEMENTS = Outpost/Outpost.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
ENABLE_APP_SANDBOX = YES; ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
ENABLE_USER_SELECTED_FILES = readonly; ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Outpost;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
@@ -454,8 +465,9 @@
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 27.0; MACOSX_DEPLOYMENT_TARGET = 27.0;
MARKETING_VERSION = 0.0.3; MARKETING_VERSION = 0.0.3;
PRODUCT_BUNDLE_IDENTIFIER = com.psmattas.Outpost; PRODUCT_BUNDLE_IDENTIFIER = com.psmattas.OutpostApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
REGISTER_APP_GROUPS = YES; REGISTER_APP_GROUPS = YES;
SDKROOT = auto; SDKROOT = auto;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -476,7 +488,7 @@
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 27.0; IPHONEOS_DEPLOYMENT_TARGET = 27.0;
MACOSX_DEPLOYMENT_TARGET = 27.0; MACOSX_DEPLOYMENT_TARGET = 27.0;
@@ -502,7 +514,7 @@
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 27.0; IPHONEOS_DEPLOYMENT_TARGET = 27.0;
MACOSX_DEPLOYMENT_TARGET = 27.0; MACOSX_DEPLOYMENT_TARGET = 27.0;
@@ -527,7 +539,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 27.0; IPHONEOS_DEPLOYMENT_TARGET = 27.0;
MACOSX_DEPLOYMENT_TARGET = 27.0; MACOSX_DEPLOYMENT_TARGET = 27.0;
@@ -552,7 +564,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B95H74ZDY6; DEVELOPMENT_TEAM = CW6GQT9SK5;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 27.0; IPHONEOS_DEPLOYMENT_TARGET = 27.0;
MACOSX_DEPLOYMENT_TARGET = 27.0; MACOSX_DEPLOYMENT_TARGET = 27.0;
+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "outpost-ios-1024.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

+11 -15
View File
@@ -3,21 +3,17 @@ import SwiftUI
struct AuthHeaderView: View { struct AuthHeaderView: View {
var body: some View { var body: some View {
VStack(spacing: 12) { VStack(spacing: 12) {
ZStack { // A plain Image Set, not the AppIcon *app icon* asset App Icon
Circle() // sets aren't reliably resolvable through Image(_:)/UIImage
.fill( // (named:) at runtime (confirmed live: showed nothing). This is
LinearGradient( // the same source artwork (outpost-ios-1024.png) duplicated
colors: [Color.accentColor, Color.accentColor.opacity(0.6)], // into a normal image set so SwiftUI can actually load it.
startPoint: .topLeading, Image("AppLogo")
endPoint: .bottomTrailing .resizable()
) .scaledToFit()
) .frame(width: 72, height: 72)
.frame(width: 64, height: 64) .clipShape(RoundedRectangle(cornerRadius: 72 * 0.2237, style: .continuous))
Image(systemName: "text.book.closed.fill") .shadow(color: .black.opacity(0.25), radius: 12, y: 6)
.font(.system(size: 26, weight: .semibold))
.foregroundStyle(.white)
}
.shadow(color: Color.accentColor.opacity(0.35), radius: 12, y: 6)
VStack(spacing: 4) { VStack(spacing: 4) {
Text("Welcome to Outpost") Text("Welcome to Outpost")
+19 -3
View File
@@ -39,11 +39,27 @@ final class SessionStore {
init(tokenStore: TokenStoring = KeychainTokenStore(), defaults: UserDefaults = .standard) { init(tokenStore: TokenStoring = KeychainTokenStore(), defaults: UserDefaults = .standard) {
self.tokenStore = tokenStore self.tokenStore = tokenStore
self.defaults = defaults self.defaults = defaults
self.isSignedIn = (try? tokenStore.token()) != nil
self.cacheStore = (try? OfflineCacheStore.makeContainer()).map(OfflineCacheStore.init(modelContainer:)) self.cacheStore = (try? OfflineCacheStore.makeContainer()).map(OfflineCacheStore.init(modelContainer:))
if isSignedIn, let serverURL { let hasToken = (try? tokenStore.token()) != nil
(apiClient, cachingClient) = Self.makeAPIClient(serverURL: serverURL, tokenStore: tokenStore, cache: cacheStore) let storedServerURL = defaults.string(forKey: Self.serverURLDefaultsKey).flatMap(URL.init(string:))
if hasToken, let storedServerURL {
isSignedIn = true
(apiClient, cachingClient) = Self.makeAPIClient(serverURL: storedServerURL, tokenStore: tokenStore, cache: cacheStore)
} else {
// Keychain and the sandboxed UserDefaults container don't
// always survive together a Keychain item written by an
// older-signed build can outlive a reinstall that wipes the
// container (or vice versa), leaving a token with no server or
// a server with no token. Clear whichever half survived rather
// than showing a broken "signed in" UI with no working
// apiClient a fresh sign-in rewrites both consistently.
if hasToken {
try? tokenStore.clear()
}
defaults.removeObject(forKey: Self.serverURLDefaultsKey)
isSignedIn = false
} }
} }