App crashes while using KMM code in Swift Custom Keyboard Target Extension

84 views Asked by At

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
0

There are 0 answers