@@ -41,6 +41,9 @@ import kotlinx.coroutines.flow.flow
41
41
import kotlinx.coroutines.flow.retryWhen
42
42
import kotlinx.coroutines.flow.single
43
43
import kotlinx.coroutines.launch
44
+ import kotlinx.coroutines.runBlocking
45
+ import kotlinx.coroutines.sync.Mutex
46
+ import kotlinx.coroutines.sync.withLock
44
47
45
48
/* *
46
49
* A listener interface allowing the consumer to be notified when either the identity or status of the identity changes
@@ -99,22 +102,6 @@ public class UID2Manager internal constructor(
99
102
*/
100
103
public var onIdentityChangedListener: UID2ManagerIdentityChangedListener ? = null
101
104
102
- /* *
103
- * Gets or sets a listener which can be used to determine if the [UID2Manager] instance has finished initializing.
104
- * Initializing includes any time required to restore a previously persisted [UID2Identity] from storage.
105
- *
106
- * If this property is set *after* initialization is complete, the callback will be invoked immediately.
107
- */
108
- public var onInitialized: (() -> Unit )? = null
109
- set(value) {
110
- field = value
111
-
112
- // If we've already finished initializing, we should immediately invoke the callback.
113
- if (initialized.isCompleted) {
114
- value?.invoke()
115
- }
116
- }
117
-
118
105
private val _state = MutableStateFlow <UID2ManagerState >(Loading )
119
106
120
107
/* *
@@ -123,8 +110,10 @@ public class UID2Manager internal constructor(
123
110
public val state: Flow <UID2ManagerState > = _state .asStateFlow()
124
111
125
112
// The Job responsible for initialising the manager. This will include de-serialising our initial state from
126
- // storage.
113
+ // storage. We allow consumers to attach a listener to detect when this Job is complete.
127
114
private var initialized: Job
115
+ private val onInitializedListeners = mutableListOf< () -> Unit > ()
116
+ private val initializedLock = Mutex ()
128
117
129
118
// An active Job that is scheduled to refresh the current identity
130
119
private var refreshJob: Job ? = null
@@ -177,6 +166,25 @@ public class UID2Manager internal constructor(
177
166
checkIdentityRefresh()
178
167
}
179
168
169
+ /* *
170
+ * Adds a listener which can be used to determine if the [UID2Manager] instance has finished initializing.
171
+ * Initializing includes any time required to restore a previously persisted [UID2Identity] from storage.
172
+ *
173
+ * If a listener is added *after* initialization is complete, the callback will be invoked immediately.
174
+ */
175
+ public fun addOnInitializedListener (listener : () -> Unit ): UID2Manager = apply {
176
+ runBlocking {
177
+ initializedLock.withLock {
178
+ // If we've already finished initializing, we should immediately invoke the callback.
179
+ if (initialized.isCompleted) {
180
+ listener()
181
+ } else {
182
+ onInitializedListeners + = listener
183
+ }
184
+ }
185
+ }
186
+ }
187
+
180
188
init {
181
189
initialized = scope.launch {
182
190
// Attempt to load the Identity from storage. If successful, we can notify any observers.
@@ -188,8 +196,7 @@ public class UID2Manager internal constructor(
188
196
validateAndSetIdentity(it.first, it.second, false )
189
197
}
190
198
191
- // If we have a callback provided, invoke it.
192
- onInitialized?.invoke()
199
+ onInitialized()
193
200
}
194
201
}
195
202
@@ -309,6 +316,17 @@ public class UID2Manager internal constructor(
309
316
}
310
317
}
311
318
319
+ /* *
320
+ * After initialization is complete, all the attached listeners will be invoked.
321
+ */
322
+ private suspend fun onInitialized () {
323
+ initializedLock.withLock {
324
+ while (onInitializedListeners.isNotEmpty()) {
325
+ onInitializedListeners.removeFirst().invoke()
326
+ }
327
+ }
328
+ }
329
+
312
330
private fun refreshIdentityInternal (identity : UID2Identity ) = scope.launch {
313
331
try {
314
332
refreshToken(identity).retryWhen { _, attempt ->
0 commit comments