From examining some crashes in crashlytics I can see that for a smattering of users there is a problem occuring when using CallKit's Call Extension, however the error code is one which isn't documented.
The crash is reported as:
libswiftCore.dylib
_diagnoseUnexpectedEnumCaseValue<A, B>(type:rawValue:) + 1272
And crash_info_entry_0 is
CXErrorCodeCallDirectoryManagerError(rawValue: 102)
The Apple documentation for CXErrorCodeCallDirectoryManagerError lists the same as in the code (https://developer.apple.com/documentation/callkit/cxerrorcodecalldirectorymanagererror)
And here are the error codes which have values 0 to 8
public enum Code : Int {
public typealias _ErrorType = CXErrorCodeCallDirectoryManagerError
case unknown = 0
case noExtensionFound = 1
case loadingInterrupted = 2
case entriesOutOfOrder = 3
case duplicateEntries = 4
case maximumEntriesExceeded = 5
case extensionDisabled = 6
#available(iOS 10.3, *)
case currentlyLoading = 7
#available(iOS 11.0, *)
case unexpectedIncrementalRemoval = 8
}
So what is error 102 and why is it not documented?
Here is my code dealing with the error codes:
CXCallDirectoryManager.sharedInstance.reloadExtension(withIdentifier: Config.callExtensionIdentifier()) { error in
if let err = error as NSError? {
self.setLastAttemptToUpdateFailedKey(failed: true)
if let cdError = error as? CXErrorCodeCallDirectoryManagerError {
var debugString = ""
var seriousError = false
switch (cdError.code)
{
case .unknown: debugString = "Unknown error"
case .noExtensionFound: debugString = "No extension found"
case .loadingInterrupted: debugString = "Loading interrupted"
seriousError = true
case .entriesOutOfOrder: debugString = "Entries out of order"
seriousError = true
case .duplicateEntries: debugString = "Duplicate Entries"
seriousError = true
case .maximumEntriesExceeded: debugString = "Maximum entries exceeded"
case .extensionDisabled: debugString = "Extension disabled"
case .currentlyLoading: debugString = "Currently Loading"
case .unexpectedIncrementalRemoval: debugString = "Unexpected Incremental Removal"
}
Related
So I have been pulling my hair out over this mysterious issue (at least for me). It only happens on App Store. I tried my best to check out my BookingViewModel codes, and I am convinced that there should be no crash there, so I did some cleaning a bit, then uploaded a new build to TestFlight, and crash happened again.
Here's a screenshot of the crash logs from Fabric's Crashlytics:
and a piece of the stacktrace:
#0. Crashed: com.apple.main-thread
0 libswiftCore.dylib 0x103e70314 swift_unknownRelease + 24
1 libswiftCore.dylib 0x103e37b5c swift_arrayDestroy + 68
2 libswiftCore.dylib 0x103c308c0 (Missing)
3 libswiftCore.dylib 0x103e40f04 swift_unownedCheck + 88
4 appname 0x102940848 BookingViewModelBookingViewModelBooking first-element-marker empty-list String (BookingViewModel.swift:167)
Lastly, the code from my BookingViewModel that has the line 167 described in the crashlog (I have edited the code for this question, I removed some lines but the idea is still there, also to make the code short):
init(_ booking: Booking, withCompleteInfoCompletion completeInfo: #escaping BookingViewModelCompleteInfoCompletion) {
let createdDate = booking.createdAt ?? ""
let username = booking.username ?? ""
let firstName = booking.firstName ?? ""
let lastName = booking.lastName ?? ""
let age = booking.age ?? ""
let birthdate = booking.birthdate ?? ""
// Final values for completion
let userDetails = "\(firstName) \(lastName) \(age) / \(birthdate)".condensedWhitespace
// Booking Status and total amount
var bookingStatus: BookingStatus = .forProcessing
if let status = booking.status,
let statusId = status.id,
let newBookingStatus = BookingStatus(rawValue: statusId) {
bookingStatus = newBookingStatus
}
var totalAmount: Double = 0.00
if bookingStatus == .forPayment || bookingStatus == .paid || bookingStatus == .forConfirmation {
if let amounts = booking.amount {
for amount in amounts {
if let amountString = amount.amount,
let amountDouble = Double(amountString) {
totalAmount = totalAmount + amountDouble
}
}
}
}
self.subtotal = String(format: "%.2f", arguments: [totalAmount])
// Payment Method
var paymentType: PaymentType = .cod
if let paymentMethod = booking.paymentMethod,
let type = PaymentType(rawValue: paymentMethod) {
paymentType = type
}
// Initial Total
let initialTotal = "₱\(self.getTotalAmount(.paypal))"
completeInfo(
"₱\(self.subtotal)", // line 167
userDetails
)
}
So question is: Is there an existing idea as to how could an app crash when it's downloaded from the App Store or Testlfight, but not when in development/debug mode (running in Xcode)?
Also, is it possible to simulate the state of the project when it's on the App Store (e.g. I've read someone's answer on SO that I could change the code signing of the project to iOS Distribution)?
Thank you!
Okay, if there's someone out there being bugged by such kind of issue, putting the line that causes the weird crash in the main thread helps!
I am not sure why though, maybe I should inspect the code more. So again, I just had to put the crashing line into the main thread like so:
DispatchQueue.main.async {
self.bookingViewModel = BookingViewModel(self.booking, withCompleteInfoCompletion: {
....
})
}
And Larme's comment above helped me a lot!
func initProducts(cateories: Categories){
products = DataServices.instance.getProducts(forCategorieTitle: cateories.nameLabel)
//productTitleName.text = cateories.nameLabel
self.productTitleName!.text = cateories.nameLabel
}
in this section i am face this error.
Use like this :
if let productTitle = self.productTitleName {
productTitle.text = cateories.nameLabel
}
This is Swift 1.2 and I'm using Xcode 6.4. The following enum has a failable initializer.
enum EstimateItemStatus: Int, Printable {
case Pending = 1
case OnHold = 2
case Done = 3
var description: String {
switch self {
case .Pending: return "Pending"
case .OnHold: return "On Hold"
case .Done: return "Done"
}
}
init?(id : Int) {
switch id {
case 1:
self = .Pending
case 2:
self = .OnHold
case 3:
self = .Done
default:
return nil
}
}
}
If I pass an ID and initialize an instance, the enum value I get is correct. However the hashValue is wrong. For example,
let status = EstimateItemStatus(id: 2)!
println("\(status.hashValue) - \(status)")
The output I get is 1 - On Hold.
But it should be 2 - On Hold.
What am I doing wrong here? Is this a compiler bug or am I missing something?
Demo playground
Maybe you're mixing up hashValue vs. rawValue.
The hash value is not enforced to be equal to the raw value
I have already authorized HealthKit, and I am getting BiologicalSex from HealthKitStore like this:
let healthKitStore:HKHealthStore = HKHealthStore()
var biologicalSexObject: HKBiologicalSexObject?
var biologicalSex: HKBiologicalSex?
do {
biologicalSexObject = try healthKitStore.biologicalSex()
biologicalSex = biologicalSexObject!.biologicalSex
} catch _ as NSError {
biologicalSex = nil
print("error reading biological sex")
}
However, when I try to print biologicalSex it returns HKBiologicalSex instead of .Male or .Female.
I have seen more or less this exact code in several tutorials, so I'm wondering if there have been any syntax changes I should be aware of in Swift 2. (The error handling has changed, so I'm curious if anything else of note has.)
The rawValue of biologicalSex = biologicalSexObject!.biologicalSex is required to do this. The enum for BiologicalSex looks like this:
typedef enum : NSInteger {
HKBiologicalSexNotSet = 0,
HKBiologicalSexFemale,
HKBiologicalSexMale,
HKBiologicalSexOther,
} HKBiologicalSex;
Using this information it is easy to design a switch statement to cover all of the possible values:
switch biologicalSex.rawValue{
case 0:
biologicalSex = nil
case 1:
biologicalSex = "Female"
case 2:
biologicalSex = "Male"
case 3:
biologicalSex = "Other"
default:
biologicalSex = nil
}
I can perform a simple Get request on a singular table within AWS dynamoDB however when I expand it to a Batch Request across multiple tables I continue to get a error
validation error detected: Value null at 'requestItems.rip.member.keys' failed to satisfy constraint
I understand this as the values not being passed correctly but I can't see what the issue is with my code
//Create Request Values
AWSDynamoDBGetItemInput *getItem = [AWSDynamoDBGetItemInput new];
AWSDynamoDBAttributeValue *hashValue = [AWSDynamoDBAttributeValue new];
hashValue.S = #"User Test";
getItem.key = #{#"ripId": hashValue};
//Create Request Values 2
AWSDynamoDBGetItemInput *getItem2 = [AWSDynamoDBGetItemInput new];
AWSDynamoDBAttributeValue *hashValue2 = [AWSDynamoDBAttributeValue new];
hashValue2.S = #"User Test";
getItem2.key = #{#"chat": hashValue2};
//Combine to Batch Request
AWSDynamoDBBatchGetItemInput * batchFetch = [AWSDynamoDBBatchGetItemInput new];
batchFetch.requestItems = #{ #"rip": getItem,
#"chat": getItem,};
[[dynamoDB batchGetItem:batchFetch] continueWithBlock:^id(BFTask *task) {
if (!task.error) {
NSLog(#"BOY SUCCES");
} else {
NSLog(#" NO BOY SUCCESS %#",task.error);
}
return nil;
}];
Searched the internet high and low but cannot see a working example of a batch request using iOS Objective C (or swift for that matter).
I have tested both variables on a single Get request and they both work.
You forgot to wrap around AWSDynamoDBAttributeValue in AWSDynamoDBKeysAndAttributes. Here is a simple example from AWSDynamoDBTests.m:
AWSDynamoDBKeysAndAttributes *keysAndAttributes = [AWSDynamoDBKeysAndAttributes new];
keysAndAttributes.keys = #[#{#"hashKey" : attributeValue1},
#{#"hashKey" : attributeValue2}];
keysAndAttributes.consistentRead = #YES;
AWSDynamoDBBatchGetItemInput *batchGetItemInput = [AWSDynamoDBBatchGetItemInput new];
batchGetItemInput.requestItems = #{table1Name: keysAndAttributes};
Since the batch get doesn't map to a class I solved it by doing this instead.
I solved it by doing this,
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
let task1 = dynamoDBObjectMapper.load(User.self, hashKey: "rtP1oQ5DJG", rangeKey: nil)
let task2 = dynamoDBObjectMapper.load(User.self, hashKey: "dbqb1zyUq1", rangeKey: nil)
AWSTask.init(forCompletionOfAllTasksWithResults: [task1, task2]).continueWithBlock { (task) -> AnyObject? in
if let users = task.result as? [User] {
print(users.count)
print(users[0].firstName)
print(users[1].firstName)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Swift 3
I was able to get the BatchGet request work with the following code. Hope this helps someone else who's struggling with the lack of Swift Docs.
This code assumes that you've configured your AWSServiceConfiguration in the AppDelegate application didFinishLaunchingWithOptions method.
let DynamoDB = AWSDynamoDB.default()
// define your primary hash keys
let hashAttribute1 = AWSDynamoDBAttributeValue()
hashAttribute1?.s = "NDlFRTdDODEtQzNCOC00QUI5LUFFMzUtRkIyNTJFNERFOTBF"
let hashAttribute2 = AWSDynamoDBAttributeValue()
hashAttribute2?.s = "MjVCNzU3MUQtMEM0NC00NEJELTk5M0YtRTM0QjVDQ0Q1NjlF"
let keys: Array = [["userID": hashAttribute1], ["userID": hashAttribute2]]
let keysAndAttributesMap = AWSDynamoDBKeysAndAttributes()
keysAndAttributesMap?.keys = keys as? [[String : AWSDynamoDBAttributeValue]]
keysAndAttributesMap?.consistentRead = true
let tableMap = ["Your-Table-Name" : keysAndAttributesMap]
let request = AWSDynamoDBBatchGetItemInput()
request?.requestItems = tableMap as? [String : AWSDynamoDBKeysAndAttributes]
request?.returnConsumedCapacity = AWSDynamoDBReturnConsumedCapacity.total
DynamoDB.batchGetItem(request!) { (output, error) in
if output != nil {
print("Batch Query output?.responses?.count:", output!.responses!)
}
if error != nil {
print("Batch Query error:", error!)
}
}