Skip to content

Commit 5873ad0

Browse files
committed
chore: ios app fixes
1 parent 6e81052 commit 5873ad0

3 files changed

Lines changed: 21 additions & 82 deletions

File tree

example/holder/app/iosApp/iosApp/Info.plist

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,14 @@
44
<dict>
55
<key>CADisableMinimumFrameDurationOnPhone</key>
66
<true/>
7+
<key>NSBluetoothAlwaysUsageDescription</key>
8+
<string>This app uses Bluetooth to communicate with credential readers for secure credential presentation.</string>
9+
<key>NSBluetoothPeripheralUsageDescription</key>
10+
<string>This app uses Bluetooth to advertise as a credential holder for secure credential presentation.</string>
11+
<key>UIBackgroundModes</key>
12+
<array>
13+
<string>bluetooth-central</string>
14+
<string>bluetooth-peripheral</string>
15+
</array>
716
</dict>
817
</plist>

example/holder/ui/core/impl/src/commonMain/kotlin/com/sphereon/kiwa/sample/ui/core/logs/LogViewerPresenterImpl.kt

Lines changed: 9 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -49,38 +49,27 @@ class LogViewerPresenterImpl(
4949

5050
@Composable
5151
override fun present(input: Unit): LogViewerPresenter.Model {
52-
var allLogs by remember { mutableStateOf<List<MobileLogEntry>>(emptyList()) }
53-
var filteredLogs by remember { mutableStateOf<List<MobileLogEntry>>(emptyList()) }
5452
var selectedLogLevel by remember { mutableStateOf(LogViewerPresenter.LogLevel.ALL) }
5553
var searchQuery by remember { mutableStateOf("") }
5654
var isSearchVisible by remember { mutableStateOf(false) }
57-
var isLoading by remember { mutableStateOf(true) }
5855
var exportMessage by remember { mutableStateOf<String?>(null) }
5956

6057
val scope = rememberCoroutineScope()
6158

6259
// Clear export message after delay
6360
SetupExportMessageClearEffect(exportMessage) { exportMessage = null }
6461

65-
// Collect logs from flow for real-time updates
66-
val logsFromFlow by mobileLogManager.logsFlow.collectAsState()
62+
// Get logs directly from StateFlow - collectAsState() returns current value synchronously
63+
// No need for separate allLogs state variable which caused async update issues
64+
val allLogs by mobileLogManager.logsFlow.collectAsState()
6765

68-
// Update local state when flow emits new logs
69-
LaunchedEffect(logsFromFlow) {
70-
allLogs = logsFromFlow
66+
// Compute filtered logs synchronously
67+
val filteredLogs = remember(allLogs, selectedLogLevel, searchQuery) {
68+
applyFilters(allLogs, selectedLogLevel, searchQuery)
7169
}
7270

73-
// Apply filtering whenever logs, level, or search query changes
74-
SetupFilteringEffect(allLogs, selectedLogLevel, searchQuery) { filtered ->
75-
filteredLogs = filtered
76-
isLoading = false
77-
}
78-
79-
// Initial load
80-
SetupInitialLoadEffect { logs ->
81-
allLogs = logs
82-
isLoading = false
83-
}
71+
// isLoading is false once we have any logs in the flow
72+
val isLoading = allLogs.isEmpty()
8473

8574
val onStateEvent = createStateEventHandler(
8675
scope = scope,
@@ -93,8 +82,6 @@ class LogViewerPresenterImpl(
9382
onSelectedLogLevelChange = { selectedLogLevel = it },
9483
onSearchQueryChange = { searchQuery = it },
9584
onSearchVisibleChange = { isSearchVisible = it },
96-
onAllLogsChange = { allLogs = it },
97-
onLoadingChange = { isLoading = it },
9885
onExportMessageChange = { exportMessage = it }
9986
)
10087

@@ -125,42 +112,6 @@ class LogViewerPresenterImpl(
125112
}
126113
}
127114

128-
@Composable
129-
private fun SetupFilteringEffect(
130-
allLogs: List<MobileLogEntry>,
131-
selectedLogLevel: LogViewerPresenter.LogLevel,
132-
searchQuery: String,
133-
onFiltered: (List<MobileLogEntry>) -> Unit
134-
) {
135-
LaunchedEffect(allLogs, selectedLogLevel, searchQuery) {
136-
val filtered = applyFilters(allLogs, selectedLogLevel, searchQuery)
137-
onFiltered(filtered)
138-
}
139-
}
140-
141-
@Composable
142-
@Suppress("TooGenericExceptionCaught", "SwallowedException")
143-
private fun SetupInitialLoadEffect(onLogsLoaded: (List<MobileLogEntry>) -> Unit) {
144-
LaunchedEffect(Unit) {
145-
try {
146-
val logs = mobileLogManager.getAllLogs()
147-
148-
if (logs.isEmpty()) {
149-
// Consider using your app to generate some logs for testing
150-
try {
151-
// Consider using your app to generate some logs for testing
152-
} catch (e: Exception) {
153-
// Error adding test logs - exception handled to prevent detekt violation
154-
// Not logging to prevent recomposition loop
155-
}
156-
}
157-
onLogsLoaded(logs)
158-
} catch (e: Exception) {
159-
// Exception handled to prevent detekt violation - not logging to prevent recomposition loop
160-
onLogsLoaded(emptyList())
161-
}
162-
}
163-
}
164115

165116
@Suppress("LongParameterList")
166117
private fun createStateEventHandler(
@@ -174,8 +125,6 @@ class LogViewerPresenterImpl(
174125
onSelectedLogLevelChange: (LogViewerPresenter.LogLevel) -> Unit,
175126
onSearchQueryChange: (String) -> Unit,
176127
onSearchVisibleChange: (Boolean) -> Unit,
177-
onAllLogsChange: (List<MobileLogEntry>) -> Unit,
178-
onLoadingChange: (Boolean) -> Unit,
179128
onExportMessageChange: (String?) -> Unit
180129
): (LogViewerPresenter.StateEvent) -> Unit = { event ->
181130
when (event) {
@@ -196,7 +145,7 @@ class LogViewerPresenterImpl(
196145
}
197146

198147
is LogViewerPresenter.StateEvent.RefreshLogs -> {
199-
handleRefreshLogs(scope, mobileLogManager, onAllLogsChange, onLoadingChange)
148+
// No-op: logs auto-update via StateFlow.collectAsState()
200149
}
201150

202151
is LogViewerPresenter.StateEvent.SelectLogLevel -> {
@@ -275,26 +224,6 @@ class LogViewerPresenterImpl(
275224
}
276225
}
277226

278-
@Suppress("TooGenericExceptionCaught", "SwallowedException")
279-
private fun handleRefreshLogs(
280-
scope: kotlinx.coroutines.CoroutineScope,
281-
mobileLogManager: MobileLogManager,
282-
onAllLogsChange: (List<MobileLogEntry>) -> Unit,
283-
onLoadingChange: (Boolean) -> Unit
284-
) {
285-
scope.launch {
286-
try {
287-
onLoadingChange(true)
288-
val logs = mobileLogManager.getAllLogs()
289-
onAllLogsChange(logs)
290-
} catch (e: Exception) {
291-
// Exception handled to prevent detekt violation - not logging to prevent recomposition loop
292-
// In production, you might want to show a user-facing error message instead
293-
} finally {
294-
onLoadingChange(false)
295-
}
296-
}
297-
}
298227

299228
private fun applyFilters(
300229
logs: List<MobileLogEntry>,

example/holder/ui/elicense/impl/src/commonMain/kotlin/com/sphereon/kiwa/sample/ui/elicense/assignment/ElicenseAssignmentPresenterImpl.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ class ElicenseAssignmentPresenterImpl(
111111
withContext(Dispatchers.Default) {
112112
// Step 2: Yield multiple times to allow any pending coroutines to complete
113113
repeat(3) { yield() }
114-
// Step 3: Wait for render cycles to fully complete (iOS needs longer)
115-
delay(300)
114+
// Step 3: Show the success screen for a readable duration (2 seconds)
115+
delay(SUCCESS_SCREEN_DISPLAY_MS)
116116
}
117117
// Step 4: Switch back to Main and yield again before navigating
118118
withContext(Dispatchers.Main) {
@@ -236,5 +236,6 @@ class ElicenseAssignmentPresenterImpl(
236236

237237
private companion object {
238238
const val DEFAULT_ERROR_MESSAGE = "Failed to assign e-license"
239+
const val SUCCESS_SCREEN_DISPLAY_MS = 2000L
239240
}
240241
}

0 commit comments

Comments
 (0)