I have a SwiftUI app, which uses a KMM shared module. The module provides useCases through which I can access some data.
import Foundation
import shared
let ChatCases = shared.ChatUseCases.shared
extension Message: Identifiable {}
class ChatViewModel: ObservableObject {
@Published var cchat: Chat
@Published var messages: [Message<MessageContent>] = []
static let shared = ChatViewModel()
init() {
self.cchat = ChatCases.createChat()
Task.detached { [weak self] in
guard let c = self?.cchat else { return }
try? await ChatCases.getMessages(c).collect<[Message<MessageContent>]> { it in
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.messages = it
}
}
}
}
func send(text: String) {
Task.detached {
try? await ChatCases.sendMessage.invoke(p1: self.cchat, p2: ModelKt.Model(name: "vgpt-c4-1"), p3: text)
}
}
}
class Collector<T>: FlowCollector {
var callback: (T) -> Void
init(callback: @escaping (T) -> Void) {
self.callback = callback
}
func emit(value: Any?) async throws {
callback(value as! T)
}
}
extension Flow {
func collect<T>(callback: @escaping (T) -> Void) async throws {
try await collect(collector: Collector<T>(callback: callback))
}
}
The same code works fine if I use it inside a regular SwiftUI app but crashes in Custom Keyboard Target Extension in the line try? await ChatCases.getMessages(c).collect. I have allowed Full Access to the keyboard as well. Here is the stack trace for the crash generated:
executeNonQuery error | error code SQLITE_CONSTRAINT
co.touchlab.sqliter.interop.SQLiteExceptionErrorCode: executeNonQuery error
at 0 shared 0x103a82ec7 kfun:co.touchlab.sqliter.interop.SQLiteExceptionErrorCode#<init>(kotlin.String;co.touchlab.sqliter.interop.SqliteDatabaseConfig;kotlin.Int){} + 111
at 1 shared 0x103a82d0f kfun:co.touchlab.sqliter.interop.ActualSqliteStatement#executeNonQuery(){}kotlin.Int + 511
at 2 shared 0x103a80ffb kfun:co.touchlab.sqliter.interop.ActualSqliteStatement#executeForChangedRowCount(){}kotlin.Int + 87
at 3 shared 0x103a8aa97 kfun:co.touchlab.sqliter.native.NativeStatement#executeUpdateDelete(){}kotlin.Int + 295
at 4 shared 0x103a7e06f kfun:co.touchlab.sqliter.concurrency.ConcurrentDatabaseConnection.ConcurrentStatement#executeUpdateDelete(){}kotlin.Int + 103
at 5 shared 0x103a8bbbf kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.$execute$lambda$1$FUNCTION_REFERENCE$1.$<bridge-BNNN>invoke(co.touchlab.sqliter.Statement){}kotlin.Long#internal + 39
at 6 shared 0x103a95c93 kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline + 203
at 7 shared 0x103a8bae3 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.$accessStatement$lambda$0$FUNCTION_REFERENCE$0.invoke#internal + 359
at 8 shared 0x103a95c93 kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline + 203
at 9 shared 0x103a8e4c7 kfun:app.cash.sqldelight.driver.native.Pool#access(kotlin.Function1<1:0,0:0>){0§<kotlin.Any?>}0:0 + 143
at 10 shared 0x103a8c89b kfun:app.cash.sqldelight.driver.native.NativeSqliteDriver#accessConnection(kotlin.Boolean;kotlin.Function1<app.cash.sqldelight.driver.native.ThreadConnection,0:0>){0§<kotlin.Any?>}0:0 + 211
at 11 shared 0x103a8b773 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.accessStatement#internal + 179
at 12 shared 0x103a8b833 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper#execute(kotlin.Int?;kotlin.String;kotlin.Int;kotlin.Function1<app.cash.sqldelight.db.SqlPreparedStatement,kotlin.Unit>?){}app.cash.sqldelight.db.QueryResult<kotlin.Long> + 143
at 13 shared 0x103ac46f3 kfun:app.cash.sqldelight.db.SqlDriver#execute(kotlin.Int?;kotlin.String;kotlin.Int;kotlin.Function1<app.cash.sqldelight.db.SqlPreparedStatement,kotlin.Unit>?){}app.cash.sqldelight.db.QueryResult<kotlin.Long>-trampoline + 203
at 14 shared 0x103a91873 kfun:ai.vyro.chat.db.models.MessageQueries#add(kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String?;kotlin.Long;kotlin.Long){} + 219
at 15 shared 0x103a9323b kfun:ai.vyro.chat.local.MessageDaoImpl#add(ai.vyro.chat.entities.Chat;ai.vyro.chat.entities.Message<ai.vyro.chat.entities.Message.Content>){}kotlin.Boolean + 367
at 16 shared 0x10399358f kfun:ai.vyro.chat.repositories.MessageRepositoryImpl.$send$lambda$3$FUNCTION_REFERENCE$2.invoke#internal + 1143
at 17 shared 0x103a98907 kfun:kotlin.coroutines.SuspendFunction2#invoke#suspend(1:0;1:1;kotlin.coroutines.Continuation<1:2>){}kotlin.Any?-trampoline + 203
at 18 shared 0x103934ed3 kfun:kotlinx.coroutines.flow.object-16.$collectCOROUTINE$2.invokeSuspend#internal + 583
at 19 shared 0x10383cd53 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 171
at 20 shared 0x10384a94b Kotlin_ObjCExport_resumeContinuationSuccess + 111
at 21 shared 0x103adcdfb invokeUnitCompletion + 175
at 22 FastypeKeyboard 0x100412f23 $s15FastypeKeyboard9CollectorC4emit5valueyypSg_tYaKFyyYacfU_ToTQ0_ + 119
at 23 libswift_Concurrency.dylib 0x1e2c33b3f _ZN5swift34runJobInEstablishedExecutorContextEPNS_3JobE + 303
at 24 libswift_Concurrency.dylib 0x1e2c34a03 _ZL17swift_job_runImplPN5swift3JobENS_11ExecutorRefE + 79
at 25 libdispatch.dylib 0x18017e30b _dispatch_root_queue_drain + 363
at 26 libdispatch.dylib 0x18017ed57 _dispatch_worker_thread2 + 231
at 27 libsystem_pthread.dylib 0x100de38e7 _pthread_wqthread + 223
at 28 libsystem_pthread.dylib 0x100de26e3 start_wqthread + 7
Sqlite operation failure UNIQUE constraint failed: DbMessage.id | error code SQLITE_CONSTRAINT
co.touchlab.sqliter.interop.SQLiteExceptionErrorCode: Sqlite operation failure UNIQUE constraint failed: DbMessage.id
at 0 shared 0x103a82ec7 kfun:co.touchlab.sqliter.interop.SQLiteExceptionErrorCode#<init>(kotlin.String;co.touchlab.sqliter.interop.SqliteDatabaseConfig;kotlin.Int){} + 111
at 1 shared 0x103a80937 kfun:co.touchlab.sqliter.interop.ActualSqliteStatement#resetStatement(){} + 643
at 2 shared 0x103a8af87 kfun:co.touchlab.sqliter.native.NativeStatement#resetStatement(){} + 327
at 3 shared 0x103a8aaf7 kfun:co.touchlab.sqliter.native.NativeStatement#executeUpdateDelete(){}kotlin.Int + 391
at 4 shared 0x103a7e06f kfun:co.touchlab.sqliter.concurrency.ConcurrentDatabaseConnection.ConcurrentStatement#executeUpdateDelete(){}kotlin.Int + 103
at 5 shared 0x103a8bbbf kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.$execute$lambda$1$FUNCTION_REFERENCE$1.$<bridge-BNNN>invoke(co.touchlab.sqliter.Statement){}kotlin.Long#internal + 39
at 6 shared 0x103a95c93 kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline + 203
at 7 shared 0x103a8bae3 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.$accessStatement$lambda$0$FUNCTION_REFERENCE$0.invoke#internal + 359
at 8 shared 0x103a95c93 kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline + 203
at 9 shared 0x103a8e4c7 kfun:app.cash.sqldelight.driver.native.Pool#access(kotlin.Function1<1:0,0:0>){0§<kotlin.Any?>}0:0 + 143
at 10 shared 0x103a8c89b kfun:app.cash.sqldelight.driver.native.NativeSqliteDriver#accessConnection(kotlin.Boolean;kotlin.Function1<app.cash.sqldelight.driver.native.ThreadConnection,0:0>){0§<kotlin.Any?>}0:0 + 211
at 11 shared 0x103a8b773 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.accessStatement#internal + 179
at 12 shared 0x103a8b833 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper#execute(kotlin.Int?;kotlin.String;kotlin.Int;kotlin.Function1<app.cash.sqldelight.db.SqlPreparedStatement,kotlin.Unit>?){}app.cash.sqldelight.db.QueryResult<kotlin.Long> + 143
at 13 shared 0x103ac46f3 kfun:app.cash.sqldelight.db.SqlDriver#execute(kotlin.Int?;kotlin.String;kotlin.Int;kotlin.Function1<app.cash.sqldelight.db.SqlPreparedStatement,kotlin.Unit>?){}app.cash.sqldelight.db.QueryResult<kotlin.Long>-trampoline + 203
at 14 shared 0x103a91873 kfun:ai.vyro.chat.db.models.MessageQueries#add(kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String?;kotlin.Long;kotlin.Long){} + 219
at 15 shared 0x103a9323b kfun:ai.vyro.chat.local.MessageDaoImpl#add(ai.vyro.chat.entities.Chat;ai.vyro.chat.entities.Message<ai.vyro.chat.entities.Message.Content>){}kotlin.Boolean + 367
at 16 shared 0x10399358f kfun:ai.vyro.chat.repositories.MessageRepositoryImpl.$send$lambda$3$FUNCTION_REFERENCE$2.invoke#internal + 1143
at 17 shared 0x103a98907 kfun:kotlin.coroutines.SuspendFunction2#invoke#suspend(1:0;1:1;kotlin.coroutines.Continuation<1:2>){}kotlin.Any?-trampoline + 203
at 18 shared 0x103934ed3 kfun:kotlinx.coroutines.flow.object-16.$collectCOROUTINE$2.invokeSuspend#internal + 583
at 19 shared 0x10383cd53 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 171
at 20 shared 0x10384a94b Kotlin_ObjCExport_resumeContinuationSuccess + 111
at 21 shared 0x103adcdfb invokeUnitCompletion + 175
at 22 FastypeKeyboard 0x100412f23 $s15FastypeKeyboard9CollectorC4emit5valueyypSg_tYaKFyyYacfU_ToTQ0_ + 119
at 23 libswift_Concurrency.dylib 0x1e2c33b3f _ZN5swift34runJobInEstablishedExecutorContextEPNS_3JobE + 303
at 24 libswift_Concurrency.dylib 0x1e2c34a03 _ZL17swift_job_runImplPN5swift3JobENS_11ExecutorRefE + 79
at 25 libdispatch.dylib 0x18017e30b _dispatch_root_queue_drain + 363
at 26 libdispatch.dylib 0x18017ed57 _dispatch_worker_thread2 + 231
at 27 libsystem_pthread.dylib 0x100de38e7 _pthread_wqthread + 223
at 28 libsystem_pthread.dylib 0x100de26e3 start_wqthread + 7
Exception doesn't match @Throws-specified class list and thus isn't propagated from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
Uncaught Kotlin exception: co.touchlab.sqliter.interop.SQLiteExceptionErrorCode: Sqlite operation failure UNIQUE constraint failed: DbMessage.id
at 0 shared 0x103a82ec7 kfun:co.touchlab.sqliter.interop.SQLiteExceptionErrorCode#<init>(kotlin.String;co.touchlab.sqliter.interop.SqliteDatabaseConfig;kotlin.Int){} + 111
at 1 shared 0x103a80937 kfun:co.touchlab.sqliter.interop.ActualSqliteStatement#resetStatement(){} + 643
at 2 shared 0x103a8af87 kfun:co.touchlab.sqliter.native.NativeStatement#resetStatement(){} + 327
at 3 shared 0x103a8aaf7 kfun:co.touchlab.sqliter.native.NativeStatement#executeUpdateDelete(){}kotlin.Int + 391
at 4 shared 0x103a7e06f kfun:co.touchlab.sqliter.concurrency.ConcurrentDatabaseConnection.ConcurrentStatement#executeUpdateDelete(){}kotlin.Int + 103
at 5 shared 0x103a8bbbf kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.$execute$lambda$1$FUNCTION_REFERENCE$1.$<bridge-BNNN>invoke(co.touchlab.sqliter.Statement){}kotlin.Long#internal + 39
at 6 shared 0x103a95c93 kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline + 203
at 7 shared 0x103a8bae3 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.$accessStatement$lambda$0$FUNCTION_REFERENCE$0.invoke#internal + 359
at 8 shared 0x103a95c93 kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline + 203
at 9 shared 0x103a8e4c7 kfun:app.cash.sqldelight.driver.native.Pool#access(kotlin.Function1<1:0,0:0>){0§<kotlin.Any?>}0:0 + 143
at 10 shared 0x103a8c89b kfun:app.cash.sqldelight.driver.native.NativeSqliteDriver#accessConnection(kotlin.Boolean;kotlin.Function1<app.cash.sqldelight.driver.native.ThreadConnection,0:0>){0§<kotlin.Any?>}0:0 + 211
at 11 shared 0x103a8b773 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper.accessStatement#internal + 179
at 12 shared 0x103a8b833 kfun:app.cash.sqldelight.driver.native.ConnectionWrapper#execute(kotlin.Int?;kotlin.String;kotlin.Int;kotlin.Function1<app.cash.sqldelight.db.SqlPreparedStatement,kotlin.Unit>?){}app.cash.sqldelight.db.QueryResult<kotlin.Long> + 143
at 13 shared 0x103ac46f3 kfun:app.cash.sqldelight.db.SqlDriver#execute(kotlin.Int?;kotlin.String;kotlin.Int;kotlin.Function1<app.cash.sqldelight.db.SqlPreparedStatement,kotlin.Unit>?){}app.cash.sqldelight.db.QueryResult<kotlin.Long>-trampoline + 203
at 14 shared 0x103a91873 kfun:ai.vyro.chat.db.models.MessageQueries#add(kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String?;kotlin.Long;kotlin.Long){} + 219
at 15 shared 0x103a9323b kfun:ai.vyro.chat.local.MessageDaoImpl#add(ai.vyro.chat.entities.Chat;ai.vyro.chat.entities.Message<ai.vyro.chat.entities.Message.Content>){}kotlin.Boolean + 367
at 16 shared 0x10399358f kfun:ai.vyro.chat.repositories.MessageRepositoryImpl.$send$lambda$3$FUNCTION_REFERENCE$2.invoke#internal + 1143
at 17 shared 0x103a98907 kfun:kotlin.coroutines.SuspendFunction2#invoke#suspend(1:0;1:1;kotlin.coroutines.Continuation<1:2>){}kotlin.Any?-trampoline + 203
at 18 shared 0x103934ed3 kfun:kotlinx.coroutines.flow.object-16.$collectCOROUTINE$2.invokeSuspend#internal + 583
at 19 shared 0x10383cd53 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 171
at 20 shared 0x10384a94b Kotlin_ObjCExport_resumeContinuationSuccess + 111
at 21 shared 0x103adcdfb invokeUnitCompletion + 175
at 22 FastypeKeyboard 0x100412f23 $s15FastypeKeyboard9CollectorC4emit5valueyypSg_tYaKFyyYacfU_ToTQ0_ + 119
at 23 libswift_Concurrency.dylib 0x1e2c33b3f _ZN5swift34runJobInEstablishedExecutorContextEPNS_3JobE + 303
at 24 libswift_Concurrency.dylib 0x1e2c34a03 _ZL17swift_job_runImplPN5swift3JobENS_11ExecutorRefE + 79
at 25 libdispatch.dylib 0x18017e30b _dispatch_root_queue_drain + 363
at 26 libdispatch.dylib 0x18017ed57 _dispatch_worker_thread2 + 231
at 27 libsystem_pthread.dylib 0x100de38e7 _pthread_wqthread + 223
at 28 libsystem_pthread.dylib 0x100de26e3 start_wqthread + 7