iOS 8 Nsinternalinconsistencyexception

iOS 8 NSInternalInconsistencyException

The bug was in the following callback:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler

The crash occurs when you call the completionHandler twice. This happens after iOS8.

NSInternalInconsistencyException: Couldn't update the Keychain Item

The solution was to setup logging of the keychain result, so I created a new method that - (void) createLogEntry:(OSStatus) result; that transcribes the result code of the SecItemAdd function into a readable message.

The full list of the Security Framework Result Codes it returns is here:
https://developer.apple.com/documentation/security/1542001-security_framework_result_codes?language=objc

- (void) writeToKeychain {
NSDictionary *attributes = NULL;
NSMutableDictionary *updateItem = NULL;
OSStatus result;

if (SecItemCopyMatching((__bridge CFDictionaryRef)genericPasswordQuery, (void *)&attributes) == noErr) {
// First we need the attributes from the Keychain.
updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
// Second we need to add the appropriate search key/values.
[updateItem setObject:[genericPasswordQuery objectForKey:(__bridge id)kSecClass] forKey:(__bridge id)kSecClass];

// Lastly, we need to set up the updated attribute list being careful to remove the class.
NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];
[tempCheck removeObjectForKey:(__bridge id)kSecClass];

#if TARGET_IPHONE_SIMULATOR
// Remove the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
//
// The access group attribute will be included in items returned by SecItemCopyMatching,
// which is why we need to remove it before updating the item.
[tempCheck removeObjectForKey:(__bridge id)kSecAttrAccessGroup];
#endif

// An implicit assumption is that you can only update a single item at a time.

result = SecItemUpdate((__bridge CFDictionaryRef)updateItem, (__bridge CFDictionaryRef)tempCheck);

[self createLogEntry:result];

NSAssert(result == noErr, @"Couldn't update the Keychain Item.");
} else {
// No previous item found; add the new one.
result = SecItemAdd((__bridge CFDictionaryRef)[self dictionaryToSecItemFormat: keychainItemData], NULL);

[self createLogEntry:result];

NSAssert(result == noErr, @"Couldn't add the Keychain Item.");
}
}

- (void) createLogEntry:(OSStatus) result {
if (@available(iOS 11.3, *)) {
NSString *message = CFBridgingRelease(SecCopyErrorMessageString(result, nil));
DDLogDebug(@"writeToKeychain result={%d} message={%@}",(int)result, message);
} else {
// Fallback on earlier versions
}
}

With the logging set up I found out that the problem was that I was reinstantiating the KeychainItemWrapper each time I needed the password to be stored/retrieved by calling:

[[KeychainItemWrapper alloc] initWithIdentifier:@"password" accessGroup:@"A123456789my.access.group"];

This caused inconsistencies between the wrapper and the keychain because it was trying to save an item that already existed. Specifically the error code errSecDuplicateItem = -25299 "The item already exists" was returned.

Core Data crash NSInternalInconsistencyException 'statement is still active'

I ended up changing around how I created the core data stack and this went away. Moving to a persistent store context, a child UI context, and a grandchild background context and persisting up the chain appropriately made this all better. I'm still not 100% sure why the original setup did not work with 8.

iOS - NSInternalInconsistencyException happens on iOS 9 and 10 but working fine on iOS 11

What's happening is that setting currentIndexPath = [] is not assigning any value to its item or section; it's creating an "empty" IndexPath (IndexPath objects are basically arbitrary-length tuples or arrays of values and can be created/manipulated as such). Any code attempting to use it in that way (such as passing it to a cellForItem call) will have potentially undefined behavior. Looks like something is effectively interpreting the missing section value as -1, and then something else is interpreting that -1 as an unsigned 64-bit integer.

Instead, if you want to use the same general logic that you have now, I'd suggest declaring indexPath as an optional:

var currentIndexPath: IndexPath?

Then, use if-let syntax in your usages of currentIndexPath:

extension MainTileViewController: MainForecastsDelegate {
func mainForecasts(_ forecastVC: MainForecastsViewController!, didChangeWith object: Any!) {
if let currentIndexPath = currentIndexPath,
let cell = outletWeatherForecastCollectionView.cellForItem(at: currentIndexPath) as? MainTileCollectionViewCell {
// Some stuff...
}
}

This follows Swift idioms and makes clear the concept that you start with an "unknown" index path.

But - as @picciano's answer suggests, you may want to rethink your overall design to better fit the larger design patterns of iOS apps.



Related Topics



Leave a reply



Submit