Ktor httpclient auth feature not working on IOS - kotlin-multiplatform

I am developing a KMM project and the authentication works well on the Android app. However when I added the Auth feature in the httpclient (located in the shared.commonMain) the ios app failled at runtime with the following message
Function doesn't have or inherit #Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
This is how I create the httpclient
private val httpclient = HttpClient() {
engine {
pipelining = true
threadsCount = 4
}
install(Logging) {
level = LogLevel.HEADERS
logger = object : Logger {
override fun log(message: String) {
Napier.v(tag = "HTTP Client", message = message)
}
}
}
install(JsonFeature) {
val json = Json { ignoreUnknownKeys = true }
serializer = KotlinxSerializer(json)
}
install(Auth) {
basic {
credentials {
BasicAuthCredentials(username = emailUser, password = passwordUser)
}
}
}
}.also {
initLogger()
}
Here is the complete code of the Greeting class:
class Greeting {
private var emailUser: String = ""
private var passwordUser: String = ""
private val httpclient = HttpClient() {
engine {
pipelining = true
threadsCount = 4
}
install(Logging) {
level = LogLevel.HEADERS
logger = object : Logger {
override fun log(message: String) {
Napier.v(tag = "HTTP Client", message = message)
}
}
}
install(JsonFeature) {
val json = Json { ignoreUnknownKeys = true }
serializer = KotlinxSerializer(json)
}
install(Auth) {
basic {
credentials {
BasicAuthCredentials(username = emailUser, password = passwordUser)
}
}
}
}.also {
initLogger()
}
#Throws(Exception::class)
suspend fun getVaccines(): List<Vaccine> {
return httpclient.get(endpointBase + Vaccine.path)
}
#Throws(Exception::class)
suspend fun loginUser(email: String, password: String): String? {
emailUser = email
passwordUser = password
return httpclient.get(endpointBase + User.path + "/userPage")
}
}
Exception full stacktrace
Function doesn't have or inherit #Throws annotation and thus exception 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: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen com.example.vaccinationcertificate_mobileapp.Greeting#3963788
at 0 iosApp 0x000000010c1f728f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95
at 1 iosApp 0x000000010c1efbbd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93
at 2 iosApp 0x000000010c1efe2d kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93
at 3 iosApp 0x000000010c2272fd kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 93
at 4 iosApp 0x000000010c228aff ThrowInvalidMutabilityException + 431
at 5 iosApp 0x000000010c3db2c0 MutationCheck + 128
at 6 iosApp 0x000000010c151165 kfun:com.example.vaccinationcertificate_mobileapp.Greeting#<init>(){} + 437
at 7 iosApp 0x000000010c17c9de objc2kotlin.883 + 142
at 8 iosApp 0x000000010c150fa3 $sSo14SharedGreetingCABycfcTO + 19
at 9 iosApp 0x000000010c14f0af $sSo14SharedGreetingCABycfC + 31
at 10 iosApp 0x000000010c150596 $s6iosApp11ContentViewVACycfC + 38 (/Users/oprisvlad2/projects/VaccinationCertificate/VaccinationCertificate-mobileapp/iosApp/iosApp/ContentView.swift:4:0)
at 11 iosApp 0x000000010c14ebf3 $s6iosApp6iOSAppV4bodyQrvgAA11ContentViewVyXEfU_ + 35 (/Users/oprisvlad2/projects/VaccinationCertificate/VaccinationCertificate-mobileapp/iosApp/iosApp/iOSApp.swift:7:4)
at 12 iosApp 0x000000010c14eda0 $s6iosApp11ContentViewVIgo_ACIegr_TR + 16
at 13 iosApp 0x000000010c14edd1 $s6iosApp11ContentViewVIgo_ACIegr_TRTA + 17
at 14 SwiftUI 0x00000001173612cf $s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfC + 63
at 15 iosApp 0x000000010c14eac5 $s6iosApp6iOSAppV4bodyQrvg + 181 (/Users/oprisvlad2/projects/VaccinationCertificate/VaccinationCertificate-mobileapp/iosApp/iosApp/iOSApp.swift:6:3)
at 16 iosApp 0x000000010c14ef79 $s6iosApp6iOSAppV7SwiftUI0B0AadEP4body4BodyQzvgTW + 9
at 17 SwiftUI 0x0000000116dce845 $s7SwiftUI15AppBodyAccessor33_A363922CEBDF47986D9772B903C8737ALLV06updateD02of7changedyx_SbtF0D0QzyXEfU_TA + 22
at 18 SwiftUI 0x0000000117357449 $s7SwiftUI12BodyAccessorPAAE03setC0yy0C0QzyXEFAFyXEfU_ + 34
at 19 SwiftUI 0x0000000116dce174 $s7SwiftUI15AppBodyAccessor33_A363922CEBDF47986D9772B903C8737ALLV06updateD02of7changedyx_SbtF + 1310
at 20 SwiftUI 0x00000001173575ac $s7SwiftUI10StaticBody33_49D2A32E637CD497C6DE29B8E060A506LLV11updateValueyyF + 161
at 21 SwiftUI 0x000000011754055c $s14AttributeGraph0A0VyACyxGqd__c5ValueQyd__RszAA12StatefulRuleRd__lufcADSPyqd__GXEfU_ySv_So11AGAttributeatcyXEfU_ySv_AJtcqd__mcfu_ySv_AJtcfu0_TA + 26
at 22 AttributeGraph 0x0000000110585e9b _ZN2AG5Graph11UpdateStack6updateEv + 553
at 23 AttributeGraph 0x0000000110586491 _ZN2AG5Graph16update_attributeENS_4data3ptrINS_4NodeEEEj + 411
at 24 AttributeGraph 0x000000011058c491 _ZN2AG5Graph20input_value_ref_slowENS_4data3ptrINS_4NodeEEENS_11AttributeIDEjPK15AGSwiftMetadataRhl + 299
at 25 AttributeGraph 0x00000001105a2889 AGGraphGetValue + 210
at 26 SwiftUI 0x00000001173574d5 $s7SwiftUI10StaticBody33_49D2A32E637CD497C6DE29B8E060A506LLV9container9ContainerQzvg + 67
at 27 SwiftUI 0x0000000117357599 $s7SwiftUI10StaticBody33_49D2A32E637CD497C6DE29B8E060A506LLV11updateValueyyF + 142
at 28 SwiftUI 0x000000011754055c $s14AttributeGraph0A0VyACyxGqd__c5ValueQyd__RszAA12StatefulRuleRd__lufcADSPyqd__GXEfU_ySv_So11AGAttributeatcyXEfU_ySv_AJtcqd__mcfu_ySv_AJtcfu0_TA + 26
at 29 AttributeGraph 0x0000000110585e9b _ZN2AG5Graph11UpdateStack6updateEv + 553
Exact solution:
moved emailUser and passwordUser in commonMain.Platform
expect var emailUser: String
expect var passwordUser: String
androidMain.Platform
actual var emailUser = ""
actual var passwordUser = ""
iosMain.Platform
actual var emailUser: String = AtomicReference("").value
actual var passwordUser: String = AtomicReference("").value

You need to check out how kotlin-native concurrent-mutability works
In short, you can't use any var in your shared code, that may be accessed from different threads. You had to wrap those values with Atomic containers. Replace both emailUser and passwordUser with something like this:
private val emailUser = Atomic("")
private val passwordUser = Atomic("")
Also you can use delegated-properties so you don't need to write .value each time
There's no Atomic declarations for common code, so you have to do you by yourself. In actual for iOS you can use native atomics and for android just make a simple wrapped.
Good news is that this won't stay for long, as JetBrains is planning to change concurrency model soon before KMP release. But for now we had to deal with it.

Ok, for inattentive guys like me I leave a fix for described issue with ktor.
Do not forget to add
dependencies {
implementation("io.ktor:ktor-client-darwin:$ktorVersion")
}
to iosMain section in build.gradle.kts for shared module.
Don't mix it with iosTest for example!
So you will have something like this:
val iosMain by creating {
dependencies {
implementation("io.ktor:ktor-client-darwin:$ktorVersion")
}
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}

Related

Invalid Mutability Exception when using background threads in KMM project on iOS

I am working on a KMM project and am currently developing a model layer. In order to work with data I was planning to create a singleton like so:
#ThreadLocal object Repository {
private var dao: DataAccessObject? = null
private val scope = CoroutineScope(Dispatchers.Main)
fun injectDao(dao: DataAccessObject) {
scope.async {
Repository.dao = dao
}
}
suspend fun create(dataObjectType: TypeOfDataObject): DataObject? {
var dataObject: DataObject? = null
val job = scope.async {
dataObject = dao?.create(dataObjectType = dataObjectType)
}
job.await()
return dataObject
}
}
In such implementation as you see the request to the database is handled in Main thread, which's quite not good. But it works and the data is returned from the function correctly. The next obvious step is to try to run it in background scope. To make it we should just redeclare scope:
private val scope = CoroutineScope(Dispatchers.Default)
When we run the code and call create function from somewhere it falls with
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.native.internal.Ref#5761ad88
2021-02-02 23:54:50.408645+0300 Plendy[28960:2893398] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
at 0 Shared 0x000000010d51640f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Throwable.kt:23:37)
at 1 Shared 0x000000010d50f0bd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
at 2 Shared 0x000000010d50f32d kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
at 3 Shared 0x000000010d5448cd kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 93 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22:60)
at 4 Shared 0x000000010d5460af ThrowInvalidMutabilityException + 431 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:92:11)
at 5 Shared 0x000000010d6470b0 MutationCheck + 128
at 6 Shared 0x000000010d5640f8 kfun:kotlin.native.internal.Ref#<set-element>(1:0){} + 104 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/native/internal/Ref.kt:12:5)
at 7 Shared 0x000000010d4a1d5b kfun:com.plendy.PlendyCore.Model.KNPlendyData.$create$lambda-1COROUTINE$4.invokeSuspend#internal + 779 (/Users/petr/Documents/Projects/Plendy/Android/Plendy/PlendyCore/src/commonMain/kotlin/com/plendy/PlendyCore/Model/KNPlendyData.kt:23:13)
at 8 Shared 0x000000010d537958 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 760 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:30:39)
at 9 Shared 0x000000010d6d7a78 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 2680 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt:106:71)
at 10 Shared 0x000000010d687fb8 kfun:kotlinx.coroutines.EventLoopImplBase#processNextEvent(){}kotlin.Long + 840 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/EventLoop.common.kt:274:18)
at 11 Shared 0x000000010d6efbbb kfun:kotlinx.coroutines#runEventLoop(kotlinx.coroutines.EventLoop?;kotlin.Function0<kotlin.Boolean>){} + 843 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/Builders.kt:80:40)
at 12 Shared 0x000000010d6f8d39 kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.start$lambda-0#internal + 409 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/Workers.kt:49:17)
at 13 Shared 0x000000010d6f8f30 kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$35.invoke#internal + 64 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/Workers.kt:47:24)
at 14 Shared 0x000000010d6f8f90 kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$35.$<bridge-UNN>invoke(){}#internal + 64 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/Workers.kt:47:24)
at 15 Shared 0x000000010d545d59 WorkerLaunchpad + 185 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:69:54)
at 16 Shared 0x000000010d64ba4f _ZN6Worker19processQueueElementEb + 3135
at 17 Shared 0x000000010d64adf6 _ZN12_GLOBAL__N_113workerRoutineEPv + 54
at 18 libsystem_pthread.dylib 0x0000000110a86950 _pthread_start + 224
at 19 libsystem_pthread.dylib 0x0000000110a8247b thread_start + 15
What is strange that the data is written to the db, meaning that dao is called successfully, but data isn't returning from the function, because the exception occurs earlier. At this point I don't understand to what frozen object does the exception relates? What I've tried next is to remove job.await() line and it worked perfectly with no exceptions besides of cause having null in function's output.
So my question is: how to make code run in a background thread still having an ability to wait for its output?
You should include more of the exception info to help figure out what's happening, and you can use ensureNeverFrozen to help identify when something is being inadvertently frozen. However, in this case, I think I can figure it out.
In this case, capturing a reference to dataObject in your background lambda will freeze it. Trying to reassign it is (probably) throwing your exception.
var dataObject: DataObject? = null
val job = scope.async {
//Trying to assign the frozen dataObject will fail
dataObject = dao?.create(dataObjectType = dataObjectType)
}
Since you're already in a suspend function, why not just use something like withContext?
suspend fun create(dataObjectType: TypeOfDataObject): DataObject? {
val dataObject = withContext(Dispatchers.Default) {
dao?.create(dataObjectType = dataObjectType)
}
return dataObject
}
And if you're going that far ...
suspend fun create(dataObjectType: TypeOfDataObject): DataObject? = withContext(Dispatchers.Default) {
dao?.create(dataObjectType = dataObjectType)
}

DSPGraph when Using CoreML

I am trying to use a SoundAnalyisis model that takes in audioSamples(Float32 15600) and returns a vggishFeature (MultiArray (Float32 12288) however I receive this error:
-01-22 10:45:43.404715+0000 SRTester[25654:891998] [DSPGraph] throwing DSPGraph::Exception with backtrace:
0 0x7fff2bdc0df9 DSPGraph::Graph::processMultiple(DSPGraph::GraphIOData*, DSPGraph::GraphIOData*) + 249
1 0x7fff2bd2223d SoundAnalysis::primeGraph(DSPGraph::Graph&, int) + 542
2 0x7fff2bcfbaae -[SNSoundClassifier primeGraph] + 134
3 0x7fff2bd052c2 -[SNAnalyzerHost primeAnalyzerGraph] + 88
4 0x7fff2bd0f268 -[SNAudioStreamAnalyzer configureAnalysisTreeWithFormat:] + 263
5 0x7fff2bd0f74b -[SNAudioStreamAnalyzer _analyzeAudioBuffer:atAudioFramePosition:] + 303
6 0x10af1cd48 _dispatch_client_callout + 8
7 0x10af2b9bf _dispatch_lane_barrier_sync_invoke_and_complete + 132
8 0x7fff2bd0f5d8 -[SNAudioStreamAnalyzer analyzeAudioBuffer:atAudioFramePosition:] + 121
9 0x10abb3116 $s8SRTester18homeViewControllerC16startAudioEngine33_CDAAA73F093090436FCAC2E152DEFC64LLyyFySo16AVAudioPCMBufferC_So0M4TimeCtcfU_yycfU_ + 326
10 0x10abb315d $sIeg_IeyB_TR + 45
11 0x10af1bdd4 _dispatch_call_block_and_release + 12
12 0x10af1cd48 _dispatch_client_callout + 8
13 0x10af235ef _dispatch_lane_serial_drain + 788
14 0x10af2417f _dispatch_lane_invoke + 422
15 0x10af2fa4e _dispatch_workloop_worker_thread + 719
[truncated?]
libc++abi.dylib: terminating with uncaught exception of type DSPGraph::Exception
(lldb)
The code throws the error in this line:
self.analyzer.analyze(buffer, atAudioFramePosition: time.sampleTime)
which belongs to this block of code:
/// Starts the audio engine
private func startAudioEngine() {
self.isLisitingForInferance = true
//requests to use the engine
do {
let request = try SNClassifySoundRequest(mlModel: soundClassifier.model)
try analyzer.add(request, withObserver: resultsObserver) // sets the results observator
} catch {
print("Unable to prepare request: \(error.localizedDescription)")
return
}
//starts a async task for the analyser
audioEngine.inputNode.installTap(onBus: 0, bufferSize: 16000, format: inputFormat) { buffer, time in
self.analysisQueue.async {
self.analyzer.analyze(buffer, atAudioFramePosition: time.sampleTime) //this line recives a SIGABRT
}
}
do{
try audioEngine.start()
}catch( _){
print("error in starting the Audio Engine")
}
}
Here is the class obsivation (although it does not even get triggered:
class ResultsObserver: NSObject, SNResultsObserving {
var delegate: iPhoneSpeakerRecongitionDelegate?
func request(_ request: SNRequest, didProduce result: SNResult) {
guard let result = result as? SNClassificationResult,
let classification = result.classifications.first else { return }
//print("here")
let confidence = classification.confidence * 100.0
//print(classification.)
if confidence > 60 {
delegate?.displayPredictionResult(identifier: classification.identifier, confidence: confidence)
}
}
}
Managed to get this to return a different error (which is where the model was incompatible)
To resolve this you have to manually move the model file into the app directory and then add it to xcode - it seems to be a bug in Xcode putting the model that is stored in another directory into the app package

backtrace miss the function that caused a crash

I use backtrace and backtrace_symbols to collect the call stack when my app crashed.
void InstallSignalHandler(void)
{
signal(SIGHUP, SignalExceptionHandler);
signal(SIGINT, SignalExceptionHandler);
signal(SIGQUIT, SignalExceptionHandler);
signal(SIGABRT, SignalExceptionHandler);
signal(SIGILL, SignalExceptionHandler);
signal(SIGSEGV, SignalExceptionHandler);
signal(SIGFPE, SignalExceptionHandler);
signal(SIGBUS, SignalExceptionHandler);
signal(SIGPIPE, SignalExceptionHandler);
}
void SignalExceptionHandler(int signal)
{
NSMutableString *mstr = [[NSMutableString alloc] init];
[mstr appendString:#"Stack:\n"];
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i <frames; ++i) {
[mstr appendFormat:#"%s\n", strs[i]];
}
NSLog(#"%#", mstr);
free(strs);
}
When I check the log in console, I just find the log miss the function that caused the crash.The function is:
+ (void)testCrash
{
int *nullPointer = NULL;
*nullPointer = 2019;
}
And the log in console is:
0 TestApp 0x0000000101d1e040 SignalExceptionHandler + 160
1 libsystem_platform.dylib 0x000000011002bb5d _sigtramp + 29
2 ??? 0x0000000000000000 0x0 + 0
3 TestApp 0x00000001019bbc6f __39+[MyCrashTesting showInViewController:]_block_invoke + 303
4 UIKit 0x000000010b09a559 -[UIAlertController _invokeHandlersForAction:] + 105
5 UIKit 0x000000010b09af5e __103-[UIAlertController _dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:]_block_invoke.461 + 16
6 UIKit 0x000000010ae42ca2 -[UIPresentationController transitionDidFinish:] + 1346
7 UIKit 0x000000010ae46b12 __56-[UIPresentationController runTransitionForCurrentState]_block_invoke.436 + 183
8 UIKit 0x000000010ba2a3b4 -[_UIViewControllerTransitionContext c
I have thought the function name "testCrash" should be in the top of the log. Does I do something wrong?
Since this is a very simple method with no arguments and no ARC heavylifting inside clang the Objective-C compiler is able to do an optimization (-O1 is enough to achieve that) of removing the message runtime call.
You can prevent this behavior with:
+ (void)testCrash __attribute__((optnone))
{
int *nullPointer = NULL;
*nullPointer = 2019;
}
The lesson learnt here is you should never rely on particular stacktrace to achieve something during your program execution. Diagnostics is fine as long as you understand the optimizing principles you've just encountered.

IOS app crash - but not in simulator - hardcoding URL works

after a successful build through xcode, the app runs in the simulator and my iPhone - when I distribute it for testing, it crashes when test users try to preform a search.
If i hardcode the URL elements - it works in both simulation mode and functions perfectly in the users test trials.
i have CrashLytics operating and it says the crash is a result of this line of code
let allContactsData = try Data(contentsOf: urlResults!)
i check in the previous VC that all the fields contain a value - and a "print" confirms the same
Company : Accountant
Suburb : Southport
State : QLD
Getting results from: http://www.myawsmurl.com.au/api/get_details.php?no=Accountant&state=QLD&suburb=Southport
var's are set at the top of the class via the following:
var toSearchFor: String = ""
var toSearchForSuburb: String = ""
var toSearchForState: String = ""
and this is the func that causes the issue:
func getResults() {
toSearchFor = searchingFor.trimmingCharacters(in: .whitespacesAndNewlines)
toSearchForSuburb = searchingSuburb.trimmingCharacters(in: .whitespacesAndNewlines)
toSearchForState = searchingState.trimmingCharacters(in: .whitespacesAndNewlines)
print("Company : \(toSearchFor)")
print("Suburb : \(toSearchForSuburb)")
print("State : \(toSearchForState)")
//toSearchFor = "Accountant"
//toSearchForSuburb = "Southport"
//toSearchForState = "QLD"
//print("Company : \(toSearchFor)")
//print("Suburb : \(toSearchForSuburb)")
//print("State : \(toSearchForState)")
let searchURL = ("http://www.myawsmurl.com.au/api/get_details.php?no=" + toSearchFor + "&state=" + toSearchForState + "&suburb=" + toSearchForSuburb)
let urlResults = URL(string:searchURL)
print("Getting results from: \(searchURL)")
do {
let allContactsData = try Data(contentsOf: urlResults!)
let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : NSArray]
etc etc
}
catch {
}
tableView.reloadData()
}
as mentioned before, if i uncomment the hardcoded vars, it runs as excepted without issues.
any help would be appreciated as i still cant figure out why it runs in simulation mode without any issues but fails on live testing even though all fields have a value.
EDIT: (crash data)
#0. Crashed: com.apple.main-thread
0 ThisAWSMAPP 0x1000752e4 ViewController.getResults() -> () (ViewController.swift:158)
1 ThisAWSMAPP 0x100076180 specialized ViewController.viewDidAppear(Bool) -> () (ViewController.swift)
2 ThisAWSMAPP 0x100071e3c #objc ViewController.viewDidAppear(Bool) -> () (ViewController.swift)
3 UIKit 0x18cb0ddb0 -[UIViewController _setViewAppearState:isAnimating:] + 856
4 UIKit 0x18cb0e31c -[UIViewController _endAppearanceTransition:] + 228
5 UIKit 0x18cbc4d64 -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] + 1224
6 UIKit 0x18cc93c5c __49-[UINavigationController _startCustomTransition:]_block_invoke + 232
7 UIKit 0x18cc1aa1c -[_UIViewControllerTransitionContext completeTransition:] + 116
8 UIKit 0x18cd68fac __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke.99 + 724
9 UIKit 0x18cb2e9d0 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 492
10 UIKit 0x18cb2e4f8 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 312
11 UIKit 0x18cb2e314 -[UIViewAnimationState animationDidStop:finished:] + 160
12 QuartzCore 0x189cdf0d4 CA::Layer::run_animation_callbacks(void*) + 260
13 libdispatch.dylib 0x18587e9a0 _dispatch_client_callout + 16
14 libdispatch.dylib 0x1858835e8 _dispatch_main_queue_callback_4CF + 996
15 CoreFoundation 0x1869750c0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
16 CoreFoundation 0x186972cdc __CFRunLoopRun + 1572
17 CoreFoundation 0x1868a2d94 CFRunLoopRunSpecific + 424
18 GraphicsServices 0x18830c074 GSEventRunModal + 100
19 UIKit 0x18cb5b130 UIApplicationMain + 208
20 ThisAWSMAPP 0x1000646d8 main (AppDelegate.swift:16)
21 libdyld.dylib 0x1858b159c start + 4
URL(string:) may return nil if the string is not a correctly formatted URL. It is not safe to force unwrap it as urlResults!, especially if you are constructing it from user input.
It safer like this:
if let url = urlResults {
let allContactsData = try Data(contentsOf:url)
// etc
} else {
// log an error for debugging
}
You should make sure the strings are URL encoded
String.trimmingCharacters only removes characters from beginning and end of the string. But your input variables may contain embedded spaces, or other illegal characters for a URL string.
Translate the input strings like this:
toSearchFor = searchingFor.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
The carsh at the given line indicates your urlResults is getting nil somehow.
Try encoding the url string before making URL object.
let toSearchFor = ""
let toSearchForSuburb = ""
let toSearchForState = ""
let searchURL = ("http://www.myawsmurl.com.au/api/get_details.php?no=" + toSearchFor + "&state=" + toSearchForState + "&suburb=" + toSearchForSuburb).addingPercentEncoding(withAllowedCharacters: . urlQueryAllowed)
if let url = searchURL, let urlResults = URL(string:url) {
print("Getting results from: \(url)")
do {
let allContactsData = try Data(contentsOf: urlResults!)
let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : NSArray]
}
catch {
}
}
Seems like thread issue. Can you try executing your code on main thread.
DispatchQueue.main.async({
let searchURL = ("http://www.myawsmurl.com.au/api/get_details.php?no=" + toSearchFor + "&state=" + toSearchForState + "&suburb=" + toSearchForSuburb)
let urlResults = URL(string:searchURL)
print("Getting results from: \(searchURL)")
do {
let allContactsData = try Data(contentsOf: urlResults!)
let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : NSArray]
etc etc
}
catch {}
self.tableView.reloadData()
})

iOS - Symbolicate Stack trace symbols

I want to symbolicate the stack trace symbols logged using [NSThread callStackSymbols] to identify the full stack backtrace calls. But, this doesn't give symbolicated trace.
0 TestApp 0x0029616f TestApp + 1823087
1 TestApp 0x003ef18d TestApp + 3236237
2 UIKit 0x2ab7bb1f <redacted> + 438
3 UIKit 0x2ac0bea3 <redacted> + 306
4 UIKit 0x2ab7bb1f <redacted> + 438
5 CoreFoundation 0x2757546d <redacted> + 48
6 CoreFoundation 0x2756e4c3 <redacted> + 234
7 UIKit 0x2ab7bc9b <redacted> + 818
8 UIKit 0x2ae32799 <redacted> + 584
9 UIKit 0x2abdfbd9 <redacted> + 308
10 UIKit 0x2ab5bdd7 <redacted> + 458
11 CoreFoundation 0x2760fffd <redacted> + 20
12 CoreFoundation 0x2760d6bb <redacted> + 278
13 CoreFoundation 0x2760dac3 <redacted> + 914
14 CoreFoundation 0x2755b3b1 CFRunLoopRunSpecific + 476
15 CoreFoundation 0x2755b1c3 CFRunLoopRunInMode + 106
16 GraphicsServices 0x2eb88201 GSEventRunModal + 136
17 UIKit 0x2abc543d UIApplicationMain + 1440
18 TestApp 0x0031581b TestApp + 2344987
19 libdyld.dylib 0x35a6baaf <redacted> + 2
I tried this with development as well as enterprise build. Neither worked out. Is there anyway to symbolicate this? I searched lot of forums and all are asking to do this with atos command like below
atos -arch armv7 -o 'app name.app'/'app name' 0x000000000
But, I am not sure whats the memory address i have to use for the above command and how to get it.
Any help would be greatly appreciated, thanks.
There's a few pieces you're skipping here. The memory addresses in atos should reference the load address and the stack address, and you'll need the dSYM file as well.
There's actually a very good writeup on apteligent called Symbolicating an iOS Crash Report about doing this manually. I would suggest reading it thoroughly to understand how symbolication works.
Here an example of how you can get binary image information at runtime.
The output of below code looks like this for example:
YourApp 0x00000001adb1e000 - arm64e - E9B05479-3D07-390C-BD36-73EEDB2B1F75
CoreGraphics 0x00000001a92dd000 - arm64e - 2F7F6EE8-635C-332A-BAC3-EFDA4894C7E2
CoreImage 0x00000001afc00000 - arm64e - CF56BCB1-9EE3-392D-8922-C8894C9F94C7
You then use the memory address of the binary image whose frame you want to symbolicate as argument in the atos command you mentioned.
Code:
import Foundation
import MachO
public struct BinaryImagesInspector {
#if arch(x86_64) || arch(arm64)
typealias MachHeader = mach_header_64
#else
typealias MachHeader = mach_header
#endif
/// Provides binary infos that are then used with the atos command to symbolicate stack traces
/// - Parameter imageNamesToLog: an optional array of binary image names to restrict the infos to
/// - Returns: An array of strings containing info on loaded binary name, its load address, architecture
/// - Note: Example:
///
/// atos -arch arm64 -o [YOUR-DSYM-ID].dSYM/Contents/Resources/DWARF/[YOUR APP] -l 0x0000000000000000 0x0000000000000000
public static func getBinaryImagesInfo(imageNamesToLog: [String]? = nil) -> [String] {
let count = _dyld_image_count()
var stringsToLog = [String]()
for i in 0..<count {
guard let dyld = _dyld_get_image_name(i) else { continue }
let dyldStr = String(cString: dyld)
let subStrings = dyldStr.split(separator: "/")
guard let imageName = subStrings.last else { continue }
if let imageNamesToLog = imageNamesToLog {
guard imageNamesToLog.contains(String(imageName)) else { continue }
}
guard let uncastHeader = _dyld_get_image_header(i) else { continue }
let machHeader = uncastHeader.withMemoryRebound(to: MachHeader.self, capacity: MemoryLayout<MachHeader>.size) { $0 }
guard let info = NXGetArchInfoFromCpuType(machHeader.pointee.cputype, machHeader.pointee.cpusubtype) else { continue }
guard let archName = info.pointee.name else { continue }
let uuid = getBinaryImageUUID(machHeader: machHeader)
let logStr = "\(imageName) \(machHeader.debugDescription) - \(String(cString: archName)) - \(uuid ?? "uuid not found")"
stringsToLog.append(logStr)
}
return stringsToLog
}
private static func getBinaryImageUUID(machHeader: UnsafePointer<MachHeader>) -> String? {
guard var header_ptr = UnsafePointer<UInt8>.init(bitPattern: UInt(bitPattern: machHeader)) else {
return nil
}
header_ptr += MemoryLayout<MachHeader>.size
guard var command = UnsafePointer<load_command>.init(bitPattern: UInt(bitPattern: header_ptr)) else {
return nil
}
for _ in 0..<machHeader.pointee.ncmds {
if command.pointee.cmd == LC_UUID {
guard let ucmd_ptr = UnsafePointer<uuid_command>.init(bitPattern: UInt(bitPattern: header_ptr)) else { continue }
let ucmd = ucmd_ptr.pointee
let cuuidBytes = CFUUIDBytes(byte0: ucmd.uuid.0,
byte1: ucmd.uuid.1,
byte2: ucmd.uuid.2,
byte3: ucmd.uuid.3,
byte4: ucmd.uuid.4,
byte5: ucmd.uuid.5,
byte6: ucmd.uuid.6,
byte7: ucmd.uuid.7,
byte8: ucmd.uuid.8,
byte9: ucmd.uuid.9,
byte10: ucmd.uuid.10,
byte11: ucmd.uuid.11,
byte12: ucmd.uuid.12,
byte13: ucmd.uuid.13,
byte14: ucmd.uuid.14,
byte15: ucmd.uuid.15)
guard let cuuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, cuuidBytes) else {
return nil
}
let suuid = CFUUIDCreateString(kCFAllocatorDefault, cuuid)
let encoding = CFStringGetFastestEncoding(suuid)
guard let cstr = CFStringGetCStringPtr(suuid, encoding) else {
return nil
}
let str = String(cString: cstr)
return str
}
header_ptr += Int(command.pointee.cmdsize)
guard let newCommand = UnsafePointer<load_command>.init(bitPattern: UInt(bitPattern: header_ptr)) else { continue }
command = newCommand
}
return nil
}
}
Further reading:
How to determine binary image architecture at runtime?
https://github.com/apple/swift/blob/master/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift
https://lowlevelbits.org/parsing-mach-o-files/
https://developer.apple.com/library/archive/technotes/tn2151/_index.html
Also available as swift package on GitHub.

Resources