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
...
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()
}
}
CodePudding user response:
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.