0

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()
    }
}
SHA
  • 81
  • 6
  • Sharing the file `OtpView.swift`, especially the method `fetchProfile()` which should include line 203, which is called in `Interceptor.swift` inside `useInteceptor(urlString:method:requestBody:completionHandler:)`. – Larme Nov 08 '21 at 13:12
  • But also, in the Organizer page, on that crash, you can double click on `closure #1 in OtpView.fetchProfile()` which should lead you exactly to the line... – Larme Nov 08 '21 at 13:13
  • @Larme I updated the question with the code. Yeah but I am unable to find what causes the issue since i cannot recreate it:( – SHA Nov 08 '21 at 13:26
  • Is that `let profile = try! JSONDecoder().decode(ProfileResponse.self, from: data)` line 203? I'd tend to say yes. And `try!` is a good reason for a crash... What if you have an JSON specific to errors, like it can the the case sometimes. I guess that woulmd make a big crash there... – Larme Nov 08 '21 at 13:28
  • Unrelated, but there is no need for `;` in Swift at the end of the line... – Larme Nov 08 '21 at 13:28
  • Yeah @Larme it's in 203 line – SHA Nov 08 '21 at 13:30
  • But why can't I recreate the issue? @Larme – SHA Nov 08 '21 at 13:31
  • I don’t know. Bad credentials, API server issue ? Bad parameters? Plenty of reasons... Stop using unwrap (use of !) and handle properly your errors... you assume that the JSON is of ProfileResponse like, but in your case it’s not... either a value not handled correctly (can it be null ?) or a totally different response... – Larme Nov 08 '21 at 13:33

1 Answers1

0

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.

Larme
  • 24,190
  • 6
  • 51
  • 81
  • Thank you @Larme. Got some exposure on what I have done wrong. Thanks again!! – SHA Nov 08 '21 at 15:35
  • And @Larme, don't know the question I am going to ask is weird, But just want to know, can we embed some message for the crash log. Like if above scenario is the issue, can we add a some sort of meaningful text? Just want to know – SHA Nov 08 '21 at 15:37
  • Not that I know of. You can use Sentry, Firebase/Crashlytics, etc, that might logs anything you want before a crash and put them into their platform. Or yourself, write a file in case of exception found, then try to upload it on next launch of the app into a specific place... But that need some work. If you know the user, maybe embed a specific TestFlight where he/she could send that log by mail... – Larme Nov 08 '21 at 15:40
  • ok thank you @Larme – SHA Nov 09 '21 at 04:33