commit def6c140a95ae67be2034828ca58e7e60f743170
parent 412a1a940cbbc016bcdecccf081ea34936a336f5
Author: triesap <tyson@radroots.org>
Date: Sun, 14 Jun 2026 01:15:19 -0700
app: route file roots through apple kit
- add the field local state AppleKit boundary
- use AppleKit file roots for log file placement
- split destructive local reset from identity-only reset
- pin the app to the AppleKit file-access package revision
Diffstat:
7 files changed, 72 insertions(+), 20 deletions(-)
diff --git a/Radroots.xcodeproj/project.pbxproj b/Radroots.xcodeproj/project.pbxproj
@@ -24,6 +24,7 @@
505A5731ACDBBB0296134340 /* TradeListingCreateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 947ADA6D32E42ED2B40A5351 /* TradeListingCreateView.swift */; };
5AECD474FB2F91855BDD79C0 /* PostFeedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F21554DA87EEC1E5C5F38365 /* PostFeedViewModel.swift */; };
657BEA5AAFF129E10177FE63 /* Nostr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63189EB90A86A9929BECD9ED /* Nostr.swift */; };
+ 6E15D30653861F26AC45B501 /* FieldLocalState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB0A2CFCBBFA9D204C6992B /* FieldLocalState.swift */; };
7C8DD424F3E3E0AB1B133863 /* RadrootsKitBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC881872F750120A184F45E6 /* RadrootsKitBindings.swift */; };
7FD8FB018DA09568303194B2 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE61264E2C98E73828E8680 /* Strings.swift */; };
8B923F78BF5B680C7F6A7CE3 /* PostFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE0EB327C10171444553378 /* PostFeedView.swift */; };
@@ -77,6 +78,7 @@
93D729E070C32490545FA837 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
947ADA6D32E42ED2B40A5351 /* TradeListingCreateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TradeListingCreateView.swift; sourceTree = "<group>"; };
9AE0EB327C10171444553378 /* PostFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostFeedView.swift; sourceTree = "<group>"; };
+ 9EB0A2CFCBBFA9D204C6992B /* FieldLocalState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldLocalState.swift; sourceTree = "<group>"; };
A0B0C9861CD86EAD3CAD549E /* View+Nav.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Nav.swift"; sourceTree = "<group>"; };
A71EFADBC7D54AF5B9314773 /* BuildConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildConfig.swift; sourceTree = "<group>"; };
ADE61264E2C98E73828E8680 /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
@@ -174,6 +176,7 @@
children = (
A71EFADBC7D54AF5B9314773 /* BuildConfig.swift */,
CA942715DB13FFD494FD35A0 /* FieldIdentityPublicMetadataStore.swift */,
+ 9EB0A2CFCBBFA9D204C6992B /* FieldLocalState.swift */,
E883FDB7004A210C9D7BE27A /* FieldRuntimeService.swift */,
8246B707FA9D218414EC4038 /* FieldSecureIdentityStore.swift */,
D4DE3DD8C3BB2F63676F463E /* LoggingSettings.swift */,
@@ -401,6 +404,7 @@
D3834AF9A4E1327B7DA557F3 /* CopyRow.swift in Sources */,
4025E63F6603011431B8A0E1 /* DebugDump.swift in Sources */,
D4CFDE54747B6D6957977025 /* FieldIdentityPublicMetadataStore.swift in Sources */,
+ 6E15D30653861F26AC45B501 /* FieldLocalState.swift in Sources */,
D25C1E1DC99F5CF8E99AE970 /* FieldRuntimeService.swift in Sources */,
D9BF5BE7E4AB5EACBF342539 /* FieldSecureIdentityStore.swift in Sources */,
1E5B41A3E1F9A7D68F63B079 /* HomeView.swift in Sources */,
diff --git a/Radroots.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Radroots.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -7,7 +7,7 @@
"location" : "git@github.com:radrootslabs/apple_kit.git",
"state" : {
"branch" : "master",
- "revision" : "d97c2badf6168e8d60289a4aef4190f820e9dfc0"
+ "revision" : "b62c0988abc15b653423153dd2a7a1f4d12ec952"
}
}
],
diff --git a/Radroots/App/AppState.swift b/Radroots/App/AppState.swift
@@ -94,7 +94,8 @@ public final class AppState: ObservableObject {
secureIdentityStore = secureStore
identityMetadataStore = metadataStore
if BuildConfig.bool(.resetLocalState) == true {
- try secureStore.resetLocalState(bundleIdentifier: try bundleIdentifier())
+ try FieldLocalState.resetFileRoots(bundleIdentifier: try bundleIdentifier())
+ try secureStore.deleteSelectedSecret()
metadataStore.delete()
try await resetRuntimeIdentityState(using: service)
applyNoIdentity()
@@ -178,7 +179,7 @@ public final class AppState: ObservableObject {
public func resetLocalIdentity() async throws {
let service = try requireRuntimeService()
- try secureIdentityStoreOrConfigured().resetLocalState(bundleIdentifier: try bundleIdentifier())
+ try secureIdentityStoreOrConfigured().deleteSelectedSecret()
try identityMetadataStoreOrConfigured().delete()
try await resetRuntimeIdentityState(using: service)
applyNoIdentity()
diff --git a/Radroots/Runtime/FieldLocalState.swift b/Radroots/Runtime/FieldLocalState.swift
@@ -0,0 +1,56 @@
+import Foundation
+import RadrootsKit
+
+enum FieldLocalStateError: LocalizedError {
+ case missingBundleIdentifier
+ case invalidLogFileName(String)
+
+ var errorDescription: String? {
+ switch self {
+ case .missingBundleIdentifier:
+ "Missing field iOS bundle identifier."
+ case .invalidLogFileName(let value):
+ "Invalid RADROOTS_FIELD_IOS_LOGGING_FILE_NAME: \(value)."
+ }
+ }
+}
+
+enum FieldLocalState {
+ static func roots(bundleIdentifier: String) throws -> RadrootsAppleFileRoots {
+ let appIdentifier = try normalizedBundleIdentifier(bundleIdentifier)
+ return try RadrootsAppleFileRoots.appContainer(appIdentifier: appIdentifier)
+ }
+
+ static func fileAccess(bundleIdentifier: String) throws -> RadrootsAppleFileAccess {
+ try RadrootsAppleFileAccess(roots: roots(bundleIdentifier: bundleIdentifier))
+ }
+
+ static func logFileURL(bundleIdentifier: String, fileName: String) throws -> URL {
+ let normalizedFileName = try normalizedLogFileName(fileName)
+ let file = RadrootsFileReference(scope: .logs, relativePath: normalizedFileName)
+ return try roots(bundleIdentifier: bundleIdentifier).resolvedURL(for: file)
+ }
+
+ static func resetFileRoots(bundleIdentifier: String) throws {
+ try fileAccess(bundleIdentifier: bundleIdentifier).resetFileRoots()
+ }
+
+ private static func normalizedBundleIdentifier(_ bundleIdentifier: String) throws -> String {
+ let trimmed = bundleIdentifier.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !trimmed.isEmpty else {
+ throw FieldLocalStateError.missingBundleIdentifier
+ }
+ return trimmed
+ }
+
+ private static func normalizedLogFileName(_ fileName: String) throws -> String {
+ let trimmed = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !trimmed.isEmpty,
+ !trimmed.contains("/"),
+ !trimmed.contains("\\"),
+ !trimmed.contains("\0") else {
+ throw FieldLocalStateError.invalidLogFileName(fileName)
+ }
+ return trimmed
+ }
+}
diff --git a/Radroots/Runtime/FieldSecureIdentityStore.swift b/Radroots/Runtime/FieldSecureIdentityStore.swift
@@ -134,19 +134,6 @@ struct FieldSecureIdentityStore {
try store.delete(Self.selectedSecretKey)
}
- func resetLocalState(bundleIdentifier: String) throws {
- let trimmedBundleIdentifier = bundleIdentifier.trimmingCharacters(in: .whitespacesAndNewlines)
- guard !trimmedBundleIdentifier.isEmpty else {
- throw FieldSecureIdentityStoreError.missingBundleIdentifier
- }
- try RadrootsAppLocalStateReset.reset(
- RadrootsAppLocalStateResetRequest(
- appIdentifier: trimmedBundleIdentifier,
- keychainServiceNames: [try Self.secureStoreServiceName(servicePrefix: servicePrefix)]
- )
- )
- }
-
static func secureStoreServiceName(servicePrefix: String) throws -> String {
try selectedSecretKey.serviceName(servicePrefix: servicePrefix)
}
diff --git a/Radroots/Runtime/LoggingSettings.swift b/Radroots/Runtime/LoggingSettings.swift
@@ -14,13 +14,17 @@ struct LoggingSettings: Equatable {
return LoggingSettings(stdout: stdout, fileEnabled: fileEnabled, fileName: fileName, level: level)
}
- func apply() throws {
+ func apply(bundleIdentifier: String) throws {
if let level {
setenv("RUST_LOG", level, 1)
}
if fileEnabled {
- let dir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.path
- try initLogging(dir: dir, fileName: fileName, isStdout: stdout)
+ let logFileURL = try FieldLocalState.logFileURL(bundleIdentifier: bundleIdentifier, fileName: fileName)
+ try initLogging(
+ dir: logFileURL.deletingLastPathComponent().path,
+ fileName: logFileURL.lastPathComponent,
+ isStdout: stdout
+ )
} else {
try initLogging(dir: nil, fileName: fileName, isStdout: stdout)
}
diff --git a/Radroots/Runtime/Radroots.swift b/Radroots/Runtime/Radroots.swift
@@ -15,7 +15,7 @@ public final class Radroots: ObservableObject {
) throws -> FieldRuntimeService {
let settings = LoggingSettings.load()
do {
- try settings.apply()
+ try settings.apply(bundleIdentifier: bundleId)
} catch {
try? initLoggingStdout()
}