field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | LICENSE

commit 2f49510b1017cae4d47fa40aa5f61d2f7349ab3c
parent 5aacafcae2d58bed72370a73f8928a9ce0ff283e
Author: triesap <tyson@radroots.org>
Date:   Thu, 11 Jun 2026 17:51:08 -0700

config: namespace runtime settings

- replace legacy relay, logging, and trade keys with RADROOTS_FIELD_IOS names
- prefer launch environment values over Info.plist build substitutions
- keep Debug local overrides separate from Release production defaults
- use wss://radroots.org as the checked-in production relay default

Diffstat:
MRadroots/Config/Base.xcconfig | 14++++++++------
MRadroots/Config/Debug.xcconfig | 13++++++++-----
MRadroots/Config/Release.xcconfig | 6------
MRadroots/Info.plist | 24++++++++++++++----------
MRadroots/Views/TradeOrderRequestView.swift | 2+-
MRadroots/radroots.xcconfig | 1-
MRadrootsKit/Sources/RadrootsKit/BuildConfig.swift | 81+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
MRadrootsKit/Sources/RadrootsKit/LoggingSettings.swift | 19++++++++++++++-----
MRadrootsKit/Sources/RadrootsKit/RelaySettings.swift | 4++--
MRadrootsKit/Sources/RadrootsKit/TradeSettings.swift | 2+-
10 files changed, 98 insertions(+), 68 deletions(-)

diff --git a/Radroots/Config/Base.xcconfig b/Radroots/Config/Base.xcconfig @@ -1,8 +1,10 @@ -#include "../radroots.xcconfig" #include "Common.xcconfig" +#include "../radroots.xcconfig" -RR_LOG_STDOUT = YES -RR_LOG_LEVEL = info -RR_LOG_FILE_ENABLED = NO -RR_LOG_FILE_NAME = radroots.log -NOSTR_RELAYS = wss:$(SLASH)$(SLASH)relay.radroots.org +RADROOTS_FIELD_IOS_RUNTIME_MODE = production +RADROOTS_FIELD_IOS_LOGGING_STDOUT = false +RADROOTS_FIELD_IOS_LOGGING_FILTER = info +RADROOTS_FIELD_IOS_LOGGING_FILE_ENABLED = true +RADROOTS_FIELD_IOS_LOGGING_FILE_NAME = field-ios.log +RADROOTS_FIELD_IOS_NOSTR_RELAY_URLS = wss:$(SLASH)$(SLASH)radroots.org +RADROOTS_FIELD_IOS_TRADE_RHI_PUBKEY = diff --git a/Radroots/Config/Debug.xcconfig b/Radroots/Config/Debug.xcconfig @@ -1,7 +1,10 @@ #include "Base.xcconfig" -RR_LOG_STDOUT = YES -RR_LOG_LEVEL = debug -RR_LOG_FILE_ENABLED = NO -RR_LOG_FILE_NAME = radroots_debug.log -NOSTR_RELAYS = ws:$(SLASH)$(SLASH)localhost:21648 +RADROOTS_FIELD_IOS_RUNTIME_MODE = localhost-dev +RADROOTS_FIELD_IOS_LOGGING_STDOUT = true +RADROOTS_FIELD_IOS_LOGGING_FILTER = debug +RADROOTS_FIELD_IOS_LOGGING_FILE_ENABLED = false +RADROOTS_FIELD_IOS_LOGGING_FILE_NAME = field-ios-debug.log +RADROOTS_FIELD_IOS_NOSTR_RELAY_URLS = ws:$(SLASH)$(SLASH)127.0.0.1:8080 + +#include? "../radroots.local.xcconfig" diff --git a/Radroots/Config/Release.xcconfig b/Radroots/Config/Release.xcconfig @@ -1,7 +1 @@ #include "Base.xcconfig" - -RR_LOG_STDOUT = NO -RR_LOG_LEVEL = info -RR_LOG_FILE_ENABLED = YES -RR_LOG_FILE_NAME = radroots.log -NOSTR_RELAYS = wss:$(SLASH)$(SLASH)relay.radroots.org diff --git a/Radroots/Info.plist b/Radroots/Info.plist @@ -57,15 +57,19 @@ <string>UIInterfaceOrientationLandscapeRight</string> </array> - <key>RR_LOG_STDOUT</key> - <string>$(RR_LOG_STDOUT)</string> - <key>RR_LOG_LEVEL</key> - <string>$(RR_LOG_LEVEL)</string> - <key>RR_LOG_FILE_ENABLED</key> - <string>$(RR_LOG_FILE_ENABLED)</string> - <key>RR_LOG_FILE_NAME</key> - <string>$(RR_LOG_FILE_NAME)</string> - <key>NOSTR_RELAYS</key> - <string>$(NOSTR_RELAYS)</string> + <key>RADROOTS_FIELD_IOS_RUNTIME_MODE</key> + <string>$(RADROOTS_FIELD_IOS_RUNTIME_MODE)</string> + <key>RADROOTS_FIELD_IOS_LOGGING_STDOUT</key> + <string>$(RADROOTS_FIELD_IOS_LOGGING_STDOUT)</string> + <key>RADROOTS_FIELD_IOS_LOGGING_FILTER</key> + <string>$(RADROOTS_FIELD_IOS_LOGGING_FILTER)</string> + <key>RADROOTS_FIELD_IOS_LOGGING_FILE_ENABLED</key> + <string>$(RADROOTS_FIELD_IOS_LOGGING_FILE_ENABLED)</string> + <key>RADROOTS_FIELD_IOS_LOGGING_FILE_NAME</key> + <string>$(RADROOTS_FIELD_IOS_LOGGING_FILE_NAME)</string> + <key>RADROOTS_FIELD_IOS_NOSTR_RELAY_URLS</key> + <string>$(RADROOTS_FIELD_IOS_NOSTR_RELAY_URLS)</string> + <key>RADROOTS_FIELD_IOS_TRADE_RHI_PUBKEY</key> + <string>$(RADROOTS_FIELD_IOS_TRADE_RHI_PUBKEY)</string> </dict> </plist> diff --git a/Radroots/Views/TradeOrderRequestView.swift b/Radroots/Views/TradeOrderRequestView.swift @@ -46,7 +46,7 @@ struct TradeOrderRequestView: View { if let errorMessage { Text(errorMessage).foregroundStyle(.red) } else if TradeSettings.rhiPubkeyOptional == nil { - Text("Set RR_TRADE_RHI_PUBKEY to enable order requests.") + Text("Set RADROOTS_FIELD_IOS_TRADE_RHI_PUBKEY to enable order requests.") .foregroundStyle(.secondary) } } diff --git a/Radroots/radroots.xcconfig b/Radroots/radroots.xcconfig @@ -1,4 +1,3 @@ PRODUCT_BUNDLE_IDENTIFIER = dev.local.radroots #include? "radroots.git.xcconfig" -#include? "radroots.local.xcconfig" diff --git a/RadrootsKit/Sources/RadrootsKit/BuildConfig.swift b/RadrootsKit/Sources/RadrootsKit/BuildConfig.swift @@ -1,44 +1,77 @@ import Foundation enum BuildConfigKey: String { - case logStdout = "RR_LOG_STDOUT" - case logLevel = "RR_LOG_LEVEL" - case logFileEnabled = "RR_LOG_FILE_ENABLED" - case logFileName = "RR_LOG_FILE_NAME" - case nostrRelays = "NOSTR_RELAYS" - case tradeRhiPubkey = "RR_TRADE_RHI_PUBKEY" + case envFile = "RADROOTS_FIELD_IOS_ENV_FILE" + case runtimeMode = "RADROOTS_FIELD_IOS_RUNTIME_MODE" + case loggingStdout = "RADROOTS_FIELD_IOS_LOGGING_STDOUT" + case loggingFilter = "RADROOTS_FIELD_IOS_LOGGING_FILTER" + case loggingFileEnabled = "RADROOTS_FIELD_IOS_LOGGING_FILE_ENABLED" + case loggingFileName = "RADROOTS_FIELD_IOS_LOGGING_FILE_NAME" + case nostrRelayUrls = "RADROOTS_FIELD_IOS_NOSTR_RELAY_URLS" + case tradeRhiPubkey = "RADROOTS_FIELD_IOS_TRADE_RHI_PUBKEY" } enum BuildConfig { static func string(_ key: BuildConfigKey) -> String? { - let info = infoString(key).map { stripOuterQuotes($0) } - let env = ProcessInfo.processInfo.environment[key.rawValue] - .flatMap { $0.trimmingCharacters(in: .whitespacesAndNewlines) } - .flatMap { $0.isEmpty ? nil : stripOuterQuotes($0) } - return info ?? env + envString(key) ?? infoString(key).map { stripOuterQuotes($0) } } static func bool(_ key: BuildConfigKey) -> Bool? { + if let env = ProcessInfo.processInfo.environment[key.rawValue], + let parsed = parseBool(env) { + return parsed + } if let v = infoValue(for: key.rawValue) { if let b = v as? Bool { return b } if let s = v as? String, let parsed = parseBool(s) { return parsed } if let n = v as? NSNumber { return n.boolValue } } - if let env = ProcessInfo.processInfo.environment[key.rawValue], - let parsed = parseBool(env) { - return parsed - } return nil } static func array(_ key: BuildConfigKey, splitBy set: CharacterSet = .whitespacesAndNewlines) -> [String]? { + if let raw = envString(key) { + return parseArray(raw, splitBy: set) + } if let direct = infoArray(key) { return direct .map { stripOuterQuotes($0).trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } } - guard var raw = string(key)?.trimmingCharacters(in: .whitespacesAndNewlines), - !raw.isEmpty else { return nil } + guard let raw = infoString(key) else { return nil } + return parseArray(raw, splitBy: set) + } + + static func effectiveDictionary(keys: [BuildConfigKey]) -> [String: Any] { + var out: [String: Any] = [:] + for k in keys { + switch k { + case .loggingStdout, .loggingFileEnabled: + if let b = bool(k) { + out[k.rawValue] = b + } + case .nostrRelayUrls: + if let arr = array(.nostrRelayUrls) { + out[k.rawValue] = arr + } + default: + if let s = string(k) { + out[k.rawValue] = s + } + } + } + return out + } + + private static func envString(_ key: BuildConfigKey) -> String? { + ProcessInfo.processInfo.environment[key.rawValue] + .flatMap { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .flatMap { $0.isEmpty ? nil : stripOuterQuotes($0) } + } + + private static func parseArray(_ value: String, splitBy set: CharacterSet) -> [String]? { + var raw = value.trimmingCharacters(in: .whitespacesAndNewlines) + guard !raw.isEmpty else { return nil } if raw.first == "[" { if let data = raw.data(using: .utf8), let arr = try? JSONSerialization.jsonObject(with: data) as? [String] { @@ -57,20 +90,6 @@ enum BuildConfig { .filter { !$0.isEmpty } } - static func effectiveDictionary(keys: [BuildConfigKey]) -> [String: Any] { - var out: [String: Any] = [:] - for k in keys { - if let b = bool(k) { - out[k.rawValue] = b - } else if k == .nostrRelays, let arr = array(.nostrRelays) { - out[k.rawValue] = arr - } else if let s = string(k) { - out[k.rawValue] = s - } - } - return out - } - private static func infoString(_ key: BuildConfigKey) -> String? { if let v = infoValue(for: key.rawValue) as? String, !v.isEmpty { return v } if let n = infoValue(for: key.rawValue) as? NSNumber { return n.stringValue } diff --git a/RadrootsKit/Sources/RadrootsKit/LoggingSettings.swift b/RadrootsKit/Sources/RadrootsKit/LoggingSettings.swift @@ -7,10 +7,10 @@ struct LoggingSettings: Equatable { var level: String? static func load() -> LoggingSettings { - let stdout = BuildConfig.bool(.logStdout) ?? true - let fileEnabled = BuildConfig.bool(.logFileEnabled) ?? false - let fileName = BuildConfig.string(.logFileName) ?? "radroots.log" - let level = BuildConfig.string(.logLevel) + let stdout = BuildConfig.bool(.loggingStdout) ?? true + let fileEnabled = BuildConfig.bool(.loggingFileEnabled) ?? false + let fileName = BuildConfig.string(.loggingFileName) ?? "field-ios.log" + let level = BuildConfig.string(.loggingFilter) return LoggingSettings(stdout: stdout, fileEnabled: fileEnabled, fileName: fileName, level: level) } @@ -27,7 +27,16 @@ struct LoggingSettings: Equatable { } func logEffectiveConfigs() { - let keys: [BuildConfigKey] = [.logStdout, .logLevel, .logFileEnabled, .logFileName, .nostrRelays] + let keys: [BuildConfigKey] = [ + .envFile, + .runtimeMode, + .loggingStdout, + .loggingFilter, + .loggingFileEnabled, + .loggingFileName, + .nostrRelayUrls, + .tradeRhiPubkey, + ] let dict = BuildConfig.effectiveDictionary(keys: keys) let json = (try? JSONSerialization.data(withJSONObject: dict, options: [.sortedKeys])) ?? Data() let text = String(data: json, encoding: .utf8) ?? String(describing: dict) diff --git a/RadrootsKit/Sources/RadrootsKit/RelaySettings.swift b/RadrootsKit/Sources/RadrootsKit/RelaySettings.swift @@ -4,13 +4,13 @@ public enum RelaySettingsError: LocalizedError { case noRelaysConfigured public var errorDescription: String? { - "No Nostr relays configured. Set build setting 'NOSTR_RELAYS'." + "No Nostr relays configured. Set 'RADROOTS_FIELD_IOS_NOSTR_RELAY_URLS'." } } public enum RelaySettings { public static func relays() throws -> [String] { - guard let parts = BuildConfig.array(.nostrRelays) else { + guard let parts = BuildConfig.array(.nostrRelayUrls) else { throw RelaySettingsError.noRelaysConfigured } let normalized = normalize(parts) diff --git a/RadrootsKit/Sources/RadrootsKit/TradeSettings.swift b/RadrootsKit/Sources/RadrootsKit/TradeSettings.swift @@ -4,7 +4,7 @@ public enum TradeSettingsError: LocalizedError { case noRhiPubkeyConfigured public var errorDescription: String? { - "No trade RHI pubkey configured. Set build setting 'RR_TRADE_RHI_PUBKEY'." + "No trade RHI pubkey configured. Set 'RADROOTS_FIELD_IOS_TRADE_RHI_PUBKEY'." } }