field_ios

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

commit ce6b27509c6f7860ccaa02e8835cf08b7b25c6b2
parent e15e04f32fcdfe1bb055b47b2dc015ea7ba9d12d
Author: triesap <tyson@radroots.org>
Date:   Thu, 11 Jun 2026 16:38:12 -0700

ios: align app shell with field runtime

Diffstat:
M.gitignore | 1-
MMakefile | 5++++-
MRadroots.xcodeproj/project.pbxproj | 27++++++++++++++++++++++++++-
MRadroots/Views/SettingsView.swift | 7+++++--
MRadroots/Views/SetupView.swift | 2+-
MRadroots/Views/TradeListingCreateView.swift | 2+-
MRadroots/Views/TradeListingDetailView.swift | 89+++++++------------------------------------------------------------------------
MRadrootsFFI/Config/ffi-build.env | 2+-
MRadrootsFFI/Makefile | 51++++++++++++++++++++++++++++++---------------------
MRadrootsFFI/source.lock | 7++++---
MRadrootsKit/Sources/RadrootsKit/AppState.swift | 11++++++-----
MRadrootsKit/Sources/RadrootsKit/RadrootsKeys.swift | 102+++++++------------------------------------------------------------------------
MRadrootsKit/Sources/RadrootsKit/TradeListing.swift | 2--
Aproject.yml | 38++++++++++++++++++++++++++++++++++++++
14 files changed, 132 insertions(+), 214 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -9,7 +9,6 @@ *.ipa *.dSYM* *.hmap -project.yml # SwiftPM .build diff --git a/Makefile b/Makefile @@ -1,6 +1,9 @@ FFI_ROOT := RadrootsFFI -.PHONY: all clean distclean sync-source build generate package install print-config +.PHONY: all clean distclean sync-source build generate package install print-config project xcodegen all clean distclean sync-source build generate package install print-config: $(MAKE) -C $(FFI_ROOT) $@ + +project xcodegen: + xcodegen generate --spec project.yml diff --git a/Radroots.xcodeproj/project.pbxproj b/Radroots.xcodeproj/project.pbxproj @@ -242,6 +242,7 @@ isa = PBXNativeTarget; buildConfigurationList = 2513910CE2A2ED545A78A163 /* Build configuration list for PBXNativeTarget "Radroots" */; buildPhases = ( + 1687531B35A5B613B89DA261 /* Generate git SHA xcconfig */, 78F7810CC77628CB591F8CD2 /* Sources */, 0FB0BFB846269AC353E4AD0E /* Resources */, 1384F9BE8BB29250A1B4A26A /* Frameworks */, @@ -266,9 +267,10 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1430; + TargetAttributes = { + }; }; buildConfigurationList = D17883D6A48F29A2A4DFE795 /* Build configuration list for PBXProject "Radroots" */; - compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -281,6 +283,7 @@ DA033E4B08F702473D7DDDE3 /* XCLocalSwiftPackageReference "RadrootsKit" */, ); preferredProjectObjectVersion = 77; + productRefGroup = 6240123423927396E47D6B3E /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( @@ -301,6 +304,28 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 1687531B35A5B613B89DA261 /* Generate git SHA xcconfig */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Generate git SHA xcconfig"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "GIT_SHA=$(git rev-parse --short HEAD || echo \"unknown\")\necho \"GIT_SHA = $GIT_SHA\" > \"$SRCROOT/Radroots/radroots.git.xcconfig\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 78F7810CC77628CB591F8CD2 /* Sources */ = { isa = PBXSourcesBuildPhase; diff --git a/Radroots/Views/SettingsView.swift b/Radroots/Views/SettingsView.swift @@ -35,7 +35,7 @@ struct SettingsView: View { if let rhi = TradeSettings.rhiPubkeyOptional { CopyRow(title: "RHI Pubkey", value: rhi) } else { - Text("Set RR_TRADE_RHI_PUBKEY to enable trade flows.") + Text("Listing publish and fetch use the shared field runtime.") .foregroundStyle(.secondary) } } @@ -66,7 +66,10 @@ struct SettingsView: View { guard let rt = radroots.runtime else { return } exportError = nil do { - let hex = try rt.keysExportSecretHex() + guard let hex = try rt.accountsExportSelectedSecretHex() else { + exportError = "No selected account has an exportable secret." + return + } UIPasteboard.general.string = hex } catch { exportError = String(describing: error) diff --git a/Radroots/Views/SetupView.swift b/Radroots/Views/SetupView.swift @@ -193,7 +193,7 @@ private struct SetupKeyView: View { Spacer() - Text("Your private key is stored securely in the iOS Keychain.") + Text("Your account is managed by the shared field runtime.") .font(.footnote) .foregroundStyle(.secondary) .multilineTextAlignment(.center) diff --git a/Radroots/Views/TradeListingCreateView.swift b/Radroots/Views/TradeListingCreateView.swift @@ -260,7 +260,7 @@ private struct ListingDraftState { func toTradeListingDraft() -> TradeListingDraft { let trimmedLabel = binLabel.trimmingCharacters(in: .whitespacesAndNewlines) - TradeListingDraft( + return TradeListingDraft( listingId: nil, farmPubkey: farmPubkey, farmDTag: farmDTag, diff --git a/Radroots/Views/TradeListingDetailView.swift b/Radroots/Views/TradeListingDetailView.swift @@ -7,7 +7,6 @@ final class TradeListingDetailViewModel: ObservableObject { @Published var messages: [TradeListingMessageSummary] = [] @Published var isLoading = false @Published var errorMessage: String? - @Published var orderId: String? init(listing: TradeListingSummary) { self.listing = listing @@ -19,14 +18,12 @@ final class TradeListingDetailViewModel: ObservableObject { errorMessage = nil let listingAddr = listing.listingAddr - let orderId = self.orderId let result: Result<[TradeListingMessageSummary], Error> = await Task.detached { @Sendable in do { return .success( try rt.tradeListingFetchMessages( listingAddr: listingAddr, - orderId: orderId, limit: 80, sinceUnix: nil ) @@ -51,9 +48,6 @@ struct TradeListingDetailView: View { @EnvironmentObject private var app: AppState let listing: TradeListingSummary @StateObject private var vm: TradeListingDetailViewModel - @State private var showOrderSheet = false - @State private var showValidationAlert = false - @State private var validationMessage = "" init(listing: TradeListingSummary) { self.listing = listing @@ -85,26 +79,14 @@ struct TradeListingDetailView: View { } Section { - SectionWideButton("Validate Listing", enabled: canUseTrade) { - sendValidationRequest() - } - - SectionWideButton("Request Order", enabled: canUseTrade, isProminent: true) { - showOrderSheet = true - } - - if let orderId = vm.orderId { - LabeledContent("Order ID", value: orderId) - .font(.footnote) - .foregroundStyle(.secondary) - } + CopyRow(title: "Listing ID", value: listing.listingId) + CopyRow(title: "Event ID", value: listing.eventId) + CopyRow(title: "Seller", value: listing.sellerPubkey) } header: { - Text("Actions") + Text("Event") } footer: { - if !canUseTrade { - Text(tradeDisabledMessage) - .foregroundStyle(.secondary) - } + Text("Order and validation request flows are retired in this field runtime pass.") + .foregroundStyle(.secondary) } Section("Activity") { @@ -118,7 +100,7 @@ struct TradeListingDetailView: View { ContentUnavailableView( "No Activity Yet", systemImage: "bubble.left.and.text.bubble.right", - description: Text("Validation and order updates appear here.") + description: Text("The current field runtime exposes listing publish and fetch first.") ) .listRowBackground(Color.clear) } else { @@ -144,19 +126,6 @@ struct TradeListingDetailView: View { } .task { await vm.refresh(app: app) } .refreshable { await vm.refresh(app: app) } - .sheet(isPresented: $showOrderSheet) { - NavigationStack { - TradeOrderRequestView(listing: listing) { result in - vm.orderId = result.orderId - Task { await vm.refresh(app: app) } - } - } - } - .alert("Validation Request", isPresented: $showValidationAlert) { - Button("OK", role: .cancel) { } - } message: { - Text(validationMessage) - } } private var priceLine: String { @@ -172,50 +141,6 @@ struct TradeListingDetailView: View { return base } - private var canUseTrade: Bool { - app.relayConnectedCount > 0 && TradeSettings.rhiPubkeyOptional != nil - } - - private var tradeDisabledMessage: String { - if app.relayConnectedCount == 0 { - return "Connect to relays to use trade flows." - } - return "Set RR_TRADE_RHI_PUBKEY to enable trade requests." - } - - private func sendValidationRequest() { - guard let rt = app.radroots.runtime else { return } - guard let rhiPubkey = TradeSettings.rhiPubkeyOptional else { - validationMessage = "Set RR_TRADE_RHI_PUBKEY to enable validation." - showValidationAlert = true - return - } - - Task { @MainActor in - let result: Result<String, Error> = await Task.detached { @Sendable in - do { - return .success( - try rt.tradeListingSendValidationRequest( - listingEventId: listing.eventId, - sellerPubkey: listing.sellerPubkey, - listingId: listing.listingId, - recipientPubkey: rhiPubkey - ) - ) - } catch { - return .failure(error) - } - }.value - - switch result { - case .success(let id): - validationMessage = "Validation request sent: \(id)" - case .failure(let error): - validationMessage = "Validation failed: \(error)" - } - showValidationAlert = true - } - } } private struct TradeListingMessageRow: View { diff --git a/RadrootsFFI/Config/ffi-build.env b/RadrootsFFI/Config/ffi-build.env @@ -1,3 +1,3 @@ # Optional overrides for local development. # SOURCE_MODE=local -# LOCAL_FFI_MANIFEST=/absolute/path/to/app-ffi-swift/Cargo.toml +# LOCAL_FFI_MANIFEST=/absolute/path/to/field_lib/crates/field_ffi_swift/Cargo.toml diff --git a/RadrootsFFI/Makefile b/RadrootsFFI/Makefile @@ -5,10 +5,13 @@ SHELL := /bin/bash -include Config/ffi-build.env SOURCE_MODE ?= git -RADROOTS_CRATES_GIT_URL ?= https://github.com/radrootslabs/crates.git -RADROOTS_CRATES_GIT_REV ?= 766c63c07ac7497d75a71aaab010fbc08ee934bc -RADROOTS_APP_FFI_CRATE_VERSION ?= 0.1.0 +RADROOTS_FIELD_LIB_GIT_URL ?= git@github.com:radrootslabs/field_lib.git +RADROOTS_FIELD_LIB_GIT_REV ?= a73f486d1dca78bfb1b1c655c2404317a7ea1e5e +RADROOTS_FIELD_FFI_CRATE_VERSION ?= 0.1.0-alpha.1 +FFI_FEATURES ?= radroots_field_core/rt,radroots_field_core/nostr-client LOCAL_FFI_MANIFEST ?= +IPHONEOS_DEPLOYMENT_TARGET ?= 18.0 +RUSTUP_TOOLCHAIN ?= 1.92.0-aarch64-apple-darwin IOS_ROOT := $(abspath $(CURDIR)/..) KIT_ROOT := $(IOS_ROOT)/RadrootsKit @@ -22,17 +25,18 @@ OUT_DIR := $(BUILD_ROOT)/out GENERATED_DIR := $(OUT_DIR)/generated HEADERS_DIR := $(OUT_DIR)/headers -CRATE_NAME := radroots-app-ffi-swift +CRATE_NAME := radroots_field_ffi_swift FFI_OUTPUT_NAME := RadrootsFFI -LIB_STEM := libradroots_app_ffi_swift +LIB_STEM := libradroots_field_ffi_swift -GIT_CRATES_DIR := $(SOURCE_ROOT)/radroots-crates -CRATE_ARCHIVE := $(SOURCE_ROOT)/$(CRATE_NAME)-$(RADROOTS_APP_FFI_CRATE_VERSION).crate -CRATE_EXTRACT_DIR := $(SOURCE_ROOT)/$(CRATE_NAME)-$(RADROOTS_APP_FFI_CRATE_VERSION) +GIT_FIELD_LIB_DIR := $(SOURCE_ROOT)/field-lib +CRATE_ARCHIVE := $(SOURCE_ROOT)/$(CRATE_NAME)-$(RADROOTS_FIELD_FFI_CRATE_VERSION).crate +CRATE_EXTRACT_DIR := $(SOURCE_ROOT)/$(CRATE_NAME)-$(RADROOTS_FIELD_FFI_CRATE_VERSION) +CARGO_FEATURE_FLAGS := $(if $(strip $(FFI_FEATURES)),--features $(FFI_FEATURES),) ifeq ($(SOURCE_MODE),git) -FFI_MANIFEST := $(GIT_CRATES_DIR)/app-ffi-swift/Cargo.toml -UNIFFI_CONFIG := $(GIT_CRATES_DIR)/app-ffi-swift/uniffi.toml +FFI_MANIFEST := $(GIT_FIELD_LIB_DIR)/crates/field_ffi_swift/Cargo.toml +UNIFFI_CONFIG := $(GIT_FIELD_LIB_DIR)/crates/field_ffi_swift/uniffi.toml SOURCE_TARGET := sync-source-git else ifeq ($(SOURCE_MODE),crates) FFI_MANIFEST := $(CRATE_EXTRACT_DIR)/Cargo.toml @@ -48,7 +52,9 @@ endif FFI_CRATE_DIR := $(dir $(FFI_MANIFEST)) -CARGO := CARGO_TARGET_DIR=$(TARGET_DIR) cargo +RUSTC_BIN := $(shell rustup which --toolchain $(RUSTUP_TOOLCHAIN) rustc) +CARGO_BIN := rustup run $(RUSTUP_TOOLCHAIN) cargo +CARGO := IPHONEOS_DEPLOYMENT_TARGET=$(IPHONEOS_DEPLOYMENT_TARGET) RUSTC=$(RUSTC_BIN) CARGO_TARGET_DIR=$(TARGET_DIR) $(CARGO_BIN) LIB_DEVICE := $(TARGET_DIR)/aarch64-apple-ios/release/$(LIB_STEM).a LIB_SIMULATOR := $(TARGET_DIR)/aarch64-apple-ios-sim/release/$(LIB_STEM).a @@ -68,6 +74,9 @@ print-config: @echo "UNIFFI_CONFIG=$(UNIFFI_CONFIG)" @echo "ARTIFACTS_DIR=$(ARTIFACTS_DIR)" @echo "GENERATED_SWIFT_DIR=$(GENERATED_SWIFT_DIR)" + @echo "IPHONEOS_DEPLOYMENT_TARGET=$(IPHONEOS_DEPLOYMENT_TARGET)" + @echo "RUSTUP_TOOLCHAIN=$(RUSTUP_TOOLCHAIN)" + @echo "RUSTC_BIN=$(RUSTC_BIN)" clean: rm -rf $(TARGET_DIR) $(OUT_DIR) $(ARTIFACTS_DIR)/$(FFI_OUTPUT_NAME).xcframework @@ -82,17 +91,17 @@ sync-source: $(SOURCE_TARGET) sync-source-git: mkdir -p $(SOURCE_ROOT) - if [ ! -d $(GIT_CRATES_DIR)/.git ]; then \ - git clone --filter=blob:none $(RADROOTS_CRATES_GIT_URL) $(GIT_CRATES_DIR); \ + if [ ! -d $(GIT_FIELD_LIB_DIR)/.git ]; then \ + git clone --filter=blob:none $(RADROOTS_FIELD_LIB_GIT_URL) $(GIT_FIELD_LIB_DIR); \ fi - git -C $(GIT_CRATES_DIR) fetch --tags --force origin - git -C $(GIT_CRATES_DIR) checkout --detach $(RADROOTS_CRATES_GIT_REV) + git -C $(GIT_FIELD_LIB_DIR) fetch --tags --force origin + git -C $(GIT_FIELD_LIB_DIR) checkout --detach $(RADROOTS_FIELD_LIB_GIT_REV) test -f $(FFI_MANIFEST) sync-source-crates: mkdir -p $(SOURCE_ROOT) rm -rf $(CRATE_EXTRACT_DIR) - curl -fLsS https://crates.io/api/v1/crates/$(CRATE_NAME)/$(RADROOTS_APP_FFI_CRATE_VERSION)/download -o $(CRATE_ARCHIVE) + curl -fLsS https://crates.io/api/v1/crates/$(CRATE_NAME)/$(RADROOTS_FIELD_FFI_CRATE_VERSION)/download -o $(CRATE_ARCHIVE) tar -xzf $(CRATE_ARCHIVE) -C $(SOURCE_ROOT) test -f $(FFI_MANIFEST) @@ -105,17 +114,17 @@ sync-source-local: test -f $(UNIFFI_CONFIG) ensure-toolchain: - rustup target add aarch64-apple-ios aarch64-apple-ios-sim + rustup target add --toolchain $(RUSTUP_TOOLCHAIN) aarch64-apple-ios aarch64-apple-ios-sim build: ensure-toolchain mkdir -p $(TARGET_DIR) $(OUT_DIR) - $(CARGO) build --manifest-path $(FFI_MANIFEST) --release --target aarch64-apple-ios - $(CARGO) build --manifest-path $(FFI_MANIFEST) --release --target aarch64-apple-ios-sim - $(CARGO) build --manifest-path $(FFI_MANIFEST) --release + $(CARGO) build --manifest-path $(FFI_MANIFEST) $(CARGO_FEATURE_FLAGS) --release --target aarch64-apple-ios + $(CARGO) build --manifest-path $(FFI_MANIFEST) $(CARGO_FEATURE_FLAGS) --release --target aarch64-apple-ios-sim + $(CARGO) build --manifest-path $(FFI_MANIFEST) $(CARGO_FEATURE_FLAGS) --release generate: mkdir -p $(GENERATED_DIR) $(HEADERS_DIR) - cd $(FFI_CRATE_DIR) && CARGO_TARGET_DIR=$(TARGET_DIR) cargo run --manifest-path Cargo.toml --bin uniffi-bindgen -- \ + cd $(FFI_CRATE_DIR) && $(CARGO) run --manifest-path Cargo.toml $(CARGO_FEATURE_FLAGS) --bin uniffi-bindgen -- \ generate --library $(LIB_HOST) \ --language swift \ --out-dir $(GENERATED_DIR) \ diff --git a/RadrootsFFI/source.lock b/RadrootsFFI/source.lock @@ -1,4 +1,5 @@ SOURCE_MODE=git -RADROOTS_CRATES_GIT_URL=https://github.com/radrootslabs/crates.git -RADROOTS_CRATES_GIT_REV=766c63c07ac7497d75a71aaab010fbc08ee934bc -RADROOTS_APP_FFI_CRATE_VERSION=0.1.0 +RADROOTS_FIELD_LIB_GIT_URL=git@github.com:radrootslabs/field_lib.git +RADROOTS_FIELD_LIB_GIT_REV=a73f486d1dca78bfb1b1c655c2404317a7ea1e5e +RADROOTS_FIELD_FFI_CRATE_VERSION=0.1.0-alpha.1 +FFI_FEATURES=radroots_field_core/rt,radroots_field_core/nostr-client diff --git a/RadrootsKit/Sources/RadrootsKit/AppState.swift b/RadrootsKit/Sources/RadrootsKit/AppState.swift @@ -54,9 +54,9 @@ public final class AppState: ObservableObject { do { try radroots.start() if let rt = radroots.runtime { - keys.loadFromKeychainIfPresent(runtime: rt) + keys.loadFromRuntime(runtime: rt) connectIfPossible() - if rt.keysIsLoaded() { + if rt.accountsHasSelectedSigningIdentity() { startPollingStatus() } } @@ -71,8 +71,9 @@ public final class AppState: ObservableObject { public func refresh() { guard let rt = radroots.runtime else { return } infoJSONString = rt.infoJson() - hasKey = rt.keysIsLoaded() - npub = rt.keysNpub() + hasKey = rt.accountsHasSelectedSigningIdentity() + npub = rt.accountsSelectedNpub() + keys.refresh(runtime: rt) updateStatus() } @@ -83,7 +84,7 @@ public final class AppState: ObservableObject { } private func connectIfPossible() { - guard let rt = radroots.runtime, rt.keysIsLoaded() else { return } + guard let rt = radroots.runtime, rt.accountsHasSelectedSigningIdentity() else { return } do { let relays = try RelaySettings.relays() try rt.nostrSetDefaultRelays(relays: relays) diff --git a/RadrootsKit/Sources/RadrootsKit/RadrootsKeys.swift b/RadrootsKit/Sources/RadrootsKit/RadrootsKeys.swift @@ -1,5 +1,4 @@ import Foundation -import Security @MainActor public final class RadrootsKeys: ObservableObject { @@ -8,105 +7,22 @@ public final class RadrootsKeys: ObservableObject { public init() {} - public func loadFromKeychainIfPresent(runtime: RadrootsRuntime) { - if let account = Keychain.activeAccount() ?? Keychain.accounts().first { - if let data = Keychain.load(service: Keychain.service, account: account), - let hex = String(data: data, encoding: .utf8) { - try? runtime.keysLoadHex32(hex: hex) - } - } - self.hasKey = runtime.keysIsLoaded() - self.npub = runtime.keysNpub() + public func loadFromRuntime(runtime: RadrootsRuntime) { + refresh(runtime: runtime) } public func generateAndPersist(runtime: RadrootsRuntime) throws { - _ = try runtime.keysGenerateInMemory() - try persistCurrentKey(runtime: runtime, accountOverride: nil) + _ = try runtime.accountsGenerate(label: "iOS", makeSelected: true) + refresh(runtime: runtime) } public func importSecretHex(hex: String, runtime: RadrootsRuntime) throws { - try runtime.keysLoadHex32(hex: hex) - try persistCurrentKey(runtime: runtime, accountOverride: nil) + _ = try runtime.accountsImportSecret(secretKey: hex, label: "iOS", makeSelected: true) + refresh(runtime: runtime) } - private func persistCurrentKey(runtime: RadrootsRuntime, accountOverride: String?) throws { - let hex = try runtime.keysExportSecretHex() - let account = accountOverride ?? runtime.keysNpub() ?? "profile-\(Int(Date().timeIntervalSince1970))" - Keychain.save(service: Keychain.service, account: account, data: Data(hex.utf8)) - Keychain.setActiveAccount(account) - self.hasKey = runtime.keysIsLoaded() - self.npub = runtime.keysNpub() - } -} - -private enum Keychain { - static let service = "com.radroots.keys" - static let activeService = "com.radroots.keys.active" - static let activeAccountKey = "active" - - static func accounts() -> [String] { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: service, - kSecMatchLimit as String: kSecMatchLimitAll, - kSecReturnAttributes as String: true - ] - var result: CFTypeRef? - let status = SecItemCopyMatching(query as CFDictionary, &result) - guard status == errSecSuccess, let items = result as? [[String: Any]] else { return [] } - return items.compactMap { $0[kSecAttrAccount as String] as? String } - } - - static func activeAccount() -> String? { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: activeService, - kSecAttrAccount as String: activeAccountKey, - kSecMatchLimit as String: kSecMatchLimitOne, - kSecReturnData as String: true - ] - var item: CFTypeRef? - let status = SecItemCopyMatching(query as CFDictionary, &item) - guard status == errSecSuccess, let data = item as? Data else { return nil } - return String(data: data, encoding: .utf8) - } - - static func setActiveAccount(_ account: String) { - let data = Data(account.utf8) - let base: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: activeService, - kSecAttrAccount as String: activeAccountKey - ] - SecItemDelete(base as CFDictionary) - var query = base - query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly - query[kSecValueData as String] = data - SecItemAdd(query as CFDictionary, nil) - } - - static func load(service: String, account: String) -> Data? { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: service, - kSecAttrAccount as String: account, - kSecMatchLimit as String: kSecMatchLimitOne, - kSecReturnData as String: true - ] - var item: CFTypeRef? - let status = SecItemCopyMatching(query as CFDictionary, &item) - guard status == errSecSuccess, let data = item as? Data else { return nil } - return data - } - - static func save(service: String, account: String, data: Data) { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: service, - kSecAttrAccount as String: account, - kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, - kSecValueData as String: data - ] - SecItemAdd(query as CFDictionary, nil) + public func refresh(runtime: RadrootsRuntime) { + self.hasKey = runtime.accountsHasSelectedSigningIdentity() + self.npub = runtime.accountsSelectedNpub() } } diff --git a/RadrootsKit/Sources/RadrootsKit/TradeListing.swift b/RadrootsKit/Sources/RadrootsKit/TradeListing.swift @@ -36,14 +36,12 @@ public extension Radroots { func tradeListingFetchMessages( listingAddr: String, - orderId: String? = nil, limit: UInt16, sinceUnix: UInt64? = nil ) throws -> [TradeListingMessageSummary] { let rt = try requireRuntime() return try rt.tradeListingFetchMessages( listingAddr: listingAddr, - orderId: orderId, limit: limit, sinceUnix: sinceUnix ) diff --git a/project.yml b/project.yml @@ -0,0 +1,38 @@ +name: Radroots + +options: + deploymentTarget: + iOS: "18.0" + +configs: + Debug: debug + Release: release + +packages: + RadrootsKit: + path: RadrootsKit + +targets: + Radroots: + type: application + platform: iOS + deploymentTarget: "18.0" + sources: + - path: Radroots + excludes: + - Info.plist + settings: + base: + SWIFT_VERSION: 6.0 + INFOPLIST_FILE: Radroots/Info.plist + configFiles: + Debug: Radroots/Config/Debug.xcconfig + Release: Radroots/Config/Release.xcconfig + dependencies: + - package: RadrootsKit + preBuildScripts: + - name: Generate git SHA xcconfig + basedOnDependencyAnalysis: false + script: | + GIT_SHA=$(git rev-parse --short HEAD || echo "unknown") + echo "GIT_SHA = $GIT_SHA" > "$SRCROOT/Radroots/radroots.git.xcconfig"