Search code examples
iosswiftxcodecrash-reportswatchos

How to find the exact exception that's being occured or the root cause from the crash logs ios


I created a watch application and it works for me fine, while others pinpoint there is a crash being occurred. So since i cannot recreate, I found the crash logs from xcode for that build, and the crash logs looks like this,

Incident Identifier: AE2591FC-4E97-4EE4-8E9A-3CA6480A2EDF
Hardware Model:      Watch5,9
Process:             watch Extension [276]
Path:                /private/var/containers/Bundle/Application/E2F3129B-4EF2-4C80-A0D4-82D246A39F32/watch.app/PlugIns/watch Extension.appex/watch Extension
Identifier:          //app id
Version:             7 (4.1.0)
AppVariant:          1:Watch5,9:7
Beta:                YES
Code Type:           ARM (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           // [378]


Date/Time:           2021-11-04 16:38:59.1335 +0530
Launch Time:         2021-11-04 16:31:54.0000 +0530
OS Version:          Watch OS 7.3.3 (18S830)
Release Type:        User
Baseband Version:    n/a
Report Version:      104

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000022ce5c88
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [276]
Triggered by Thread:  0

Thread 0 name:
Thread 0 Crashed:
0   libswiftCore.dylib              0x22ce5c88 _assertionFailure(_:_:file:line:flags:) + 356 (AssertCommon.swift:132)
1   libswiftCore.dylib              0x22d3d12c swift_unexpectedError + 300 (ErrorType.swift:188)
2   watch Extension                 0x04488074 closure #1 in OtpView.fetchProfile() + 1232 (OtpView.swift:203)
3   watch Extension                 0x0443c47c specialized closure #1 in closure #1 in useInteceptor(urlString:method:requestBody:completionHandler:) + 1120 (Inteceptor.swift:0)
4   watch Extension                 0x0443dc80 partial apply for specialized closure #1 in closure #1 in useInteceptor(urlString:method:requestBody:completionHandler:) + 72 (<compiler-generated>:0)
5   watch Extension                 0x04438a38 thunk for @escaping @callee_guaranteed () -> () + 20 (<compiler-generated>:0)
6   libdispatch.dylib               0x2a5901bc _dispatch_client_callout + 16 (object.m:559)
7   libdispatch.dylib               0x2a593344 _dispatch_continuation_pop + 492 (inline_internal.h:2548)
8   libdispatch.dylib               0x2a5a3eb0 _dispatch_source_invoke + 1272 (source.c:570)
9   libdispatch.dylib               0x2a59ce58 _dispatch_main_queue_callback_4CF + 556 (inline_internal.h:2589)
10  CoreFoundation                  0x20110eec __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12 (CFRunLoop.c:1790)
11  CoreFoundation                  0x2010b428 __CFRunLoopRun + 2452 (CFRunLoop.c:3118)
12  CoreFoundation                  0x2010a574 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3242)
13  GraphicsServices                0x348904c0 GSEventRunModal + 160 (GSEvent.c:2259)
14  UIKitCore                       0x24f95adc -[UIApplication _run] + 1104 (UIApplication.m:3253)
15  UIKitCore                       0x24f9afdc UIApplicationMain + 140 (UIApplication.m:4707)
16  WatchKit                        0x2703dc30 WKApplicationExtensionMain + 312 (main.m:131)
17  SwiftUI                         0x28a3b628 runApp<A>(_:) + 196 (WatchKitRenderer.swift:34)
18  SwiftUI                         0x28957d6c static App.main() + 80 (App.swift:113)
19  watch Extension                 0x044288c8 $main + 24 (<compiler-generated>:10)
20  watch Extension                 0x044288c8 main + 36 (Observables.swift:0)
21  WatchKit                        0x26f6694c WKExtensionMain + 608 (main.m:102)
22  libdyld.dylib                   0x3591191c start + 4
...

enter image description here

I ain't sure where the error is happening and why it's happening. Can anyone will be able to guide me through this?

And my swift code for the relevant issue looks like this,

/*
 *   © Copyrights 
 *   All Rights Reserved.
 *
 *   
 *   
 *   
 *
 *   
 *
 *   
 */

import SwiftUI

struct OtpView: View {
    private let placeholder: String = "Enter the 6 digit OTP here"
    @State private var input:String = "Enter the 6 digit OTP here";
    @State private var navigateToNext: Bool = false;
    @State private var isVisible: Bool = false;
    @State private var alertMessage: String = "";
    @State private var isFetching: Bool = false;
    
    init(){
        input=self.placeholder;
    }
    
    var body: some View {
        //        NavigationView{
        ZStack{
            AlertView(isVisible: $isVisible,
                      message: self.alertMessage,
                      onHide: {
                        if self.isVisible {
                            self.isVisible.toggle();
                        }
                      })
            
            if self.isFetching {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            }
            
            GeometryReader{
                geometry in
                
                let width =  geometry.size.width;
                let height = geometry.size.height;
                
                VStack(alignment:.leading){
                    
                    Text(input)
                        .font(.system(size:14.0,design: .rounded))
                        .multilineTextAlignment(.trailing)
                        .frame(width:width)
                        .lineLimit(1)
                    
                    NavigationLink(destination:HomeView(),
                                   isActive: $navigateToNext){
                        
                    }
                    .frame(width:0,height:0)
                    
                    HStack(){
                        Spacer()
                        GradientButton(buttonTitle: "NEXT"){
                            self.validateOtp()
                        }
                        Spacer()
                    }
                    .frame(width: width)
                    .padding([.bottom,.top], 5.0)
                    
                    Divider()
                    Spacer()
                    
                    VStack(){
                        ForEach(buttonList, id: \.self){
                            row in
                            HStack(){
                                ForEach(row, id:\.self){
                                    button in
                                    
                                    Button(action: {
                                        if self.input == self.placeholder {
                                            if button != "del"{
                                                input = button;
                                            }
                                        }else{
                                            if button != "del" {
                                                input.count != 6 ?
                                                    input += button : nil
                                            }else{
                                                if input.count == 1 {
                                                    input = self.placeholder
                                                }else{
                                                    input = String(input.dropLast())
                                                }
                                            }
                                        }
                                    }){
                                        if button != "del" {
                                            Text(button)
                                                .font(.system(size:14.0,design: .rounded))
                                                .frame(minWidth:(width/4), idealWidth: (width/4), maxWidth:(width/4),
                                                       maxHeight:.infinity)
                                        } else {
                                            Image("Delete")
                                                .resizable()
                                                .frame(width:18, height:16)
                                                .padding()
                                        }
                                    }
                                    .frame(minWidth:(width/4), idealWidth: (width/4), maxWidth:width/3-10,
                                           minHeight:height/6,
                                           idealHeight: height/6,
                                           maxHeight: height/6)
                                    .cornerRadius(50.0)
                                    .buttonStyle(PlainButtonStyle())
                                    
                                    if button != "3" && button != "6" && button != "9" && button != "del" {
                                        Spacer()
                                    }
                                }
                            }
                        }
                    }
                }
            }
            .blur(radius: isVisible || self.isFetching ? 3.0 : 0)
            .disabled(isVisible)
        }
        
        //        }
        .navigationBarBackButtonHidden(false)
        .navigationTitle("OTP")
        .onDisappear(perform: {
            print("GOINF FROM OTP")
        })
    }
    
    func validateOtp(){
        if input != self.placeholder && input.count == 6 && isStringContainsOnlyNumbers(string:input){
            
            self.isFetching.toggle();
            let requestHeader = getRequestHeader(action:”NO_ACTION”);
            
            guard let id = requestHeader[“id”] else {
                return;
            }
            
            let request = [
                “Validate”:[
                    “name”:GlobalData.name,
                    "deviceId":"N/A",
                    "detail”: [
                        "action”:”NO_ACTION”,
                        "otp":self.input
                    ],
                ],
                "requestHeader": requestHeader
            ] as [String : Any]
            
            useInteceptor(urlString: ncellUrls[“URL1”] ?? "",method: "POST", requestBody: request){  data, error, displayCode in
                self.isFetching.toggle();
                if let data = data  {
                    let validateOtpResponse = try! JSONDecoder().decode(ValidateOtpResponse.self, from: data);
                    
                    
                    if let userAuth = validateOtpResponse.validateOTPResponse {
                        
                        
                        
                       
                        
                        self.isFetching.toggle();
                        self.fetchProfile();
                        //                        self.navigateToNext.toggle();
                    }
                } else {
                    self.alertMessage = getErrorMessage(code: displayCode)
                    self.isVisible.toggle();
                }
            }
        }else{
            self.alertMessage = "Please enter a valid Otp to proceed."
            self.isVisible.toggle();
        }
    }
    
    func fetchProfile(){
        let request = [
            “getProfile”:[
                “name”:GlobalData.name,
                “Id”: "1",
            ],
            "requestHeader": getRequestHeader(action:”ACTION1”)
        ]
        
        useInteceptor(urlString: ncellUrls[“URL2”] ?? "",method: "POST", requestBody: request){  data, error, displayCode in
            self.isFetching.toggle();
            if let data = data  {
                let profile = try! JSONDecoder().decode(ProfileResponse.self, from: data);
                if let ratePlanDetails = profile.response?.detail {
                    
                    
                    
                    
                    self.navigateToNext.toggle();
                }
            } else {
                self.alertMessage = getErrorMessage(code: displayCode)
                self.isVisible.toggle();
            }
        }
    }
}

struct OtpView_Previews: PreviewProvider {
    static var previews: some View {
        OtpView()
    }
}

Solution

  • Unrelated, but in Swift, there is no need for ; at the end of the lines.

    Let's read the crash log:

    2   watch Extension                 0x04488074 closure #1 in OtpView.fetchProfile() + 1232 (OtpView.swift:203)
    3   watch Extension                 0x0443c47c specialized closure #1 in closure #1 in useInteceptor(urlString:method:requestBody:completionHandler:) + 1120 (Inteceptor.swift:0)
    4   watch Extension                 0x0443dc80 partial apply for specialized closure #1 in closure #1 in useInteceptor(urlString:method:requestBody:completionHandler:) + 72 (<compiler-generated>:0)
    

    That's the interesting part (the top ones with names of methods), you read them from the bottom.

    So in Interceptor.swift, method useInteceptor(urlString:method:requestBody:completionHandler:) is called at some point.
    In this one, there is a closure (or multiple ones), but at least, in the first one (closure #1), which is completionHandler, that you call in fetchProfile of OptView class in OptView.swift file.
    And there, in line 203, it's the line causing the crash.

    Let's analyze the culprit:

    let profile = try! JSONDecoder().decode(ProfileResponse.self, from: data)
    

    Here, the possible crash is because of try!.
    Why did you use try! instead of writing a do/try/catch? Do you know why you used a force unwrap ! here? What it means?

    It means that if an error is thrown, just make the app crash.

    So if there is a crash, it's expected behavior, since you explicitly wrote "crash here if there is thrown error".

    So, now, why would decode(_:from:) crash?
    I don't know what's ProfileResponse, but that method could throw an error because you didn't specify to it that a value in the JSON can be nul, a value in the JSON can be omitted, because there is another issue with the received JSON, or JSON is invalid.
    Or, because your API is giving a bad value. It's sometimes the cases when API encounters an error, they could responds: {"error": "some reason why it failed"}. It's a valid JSON, but I don't think that ProfileResponse expect to be like that.
    Now, as why it giving bad response, it's up to your Web API, check the doc, check the API developers for possible responses: Did you use bad parameters, are you falling into the one case not handled by back-end?

    So when you wrote that line with the try!, you decided to tell: "Don't worry about the response, if there is a response, I'm sure of it that it can be decoded into a ProfileResponse object. Trust me, I know what I'm doing, I guarantee it will be always valid, and if that's not the case, just crash, that would prove me wrong, but rest assured, I'm sure of myself". Yes, that what meant try!.

    If you don't want to crash, don't use !, and write a property do/try/catch.

    do {
        let profile = try JSONDecoder().decode(ProfileResponse.self, from: data)
        if let ratePlanDetails = profile.response?.detail {     
            self.navigateToNext.toggle()
        }
    } catch {
        print("Oops, there was an error while decoding ProfileResponse: \(error)")
        print("And the API response was: \(String(data: data, encoding: .utf8) ?? "unknown data")")
    }
    

    Now, as to why you have an invalid response, that's up to your debugging: trying to reproduce it, with specific params, specific case, etc.). We can't guess what's wrong, we can't guess what's returning the API. Once you know what's the real response sent back by your API, and don't know how to handle it, you can ask a new question on SO and we might help you, but at this point, we can't do anything more about your issue.