CoreData HeavyWeight Migration Issues

We recently hit a pretty severe bug. In one of our apps, users began experiencing token issues after an upgrade.

Bottomline?… the last upgrade had a Heavyweight migration. So far so good, but what happened?. Turns out that the URIRepresentation that can be used to map a NSManagedObjectID, is and is not reliable. Everything is okay, until you perform a heavyweight migration!.

Heavyweight migrations might swizzle your NSManagedObjectID’s. Fix?, create your own primaryKeys. NSUUID helper class is the easiest way to accomplish that.

Reference here!

TextKit + iOS Clipping Bug

I’ve just spent several hours hunting an iOS 8 specific bug. After inserting several new lines into a UITextView (with custom TextKit stack), the newly-added text would not appear.

This is the way our NSTextContainer instance was being initialized

@implementation MyTextView

- (instancetype)init {
   
    SPInteractiveTextStorage *textStorage = [[MyInteractiveTextStorage alloc] init];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
   
    NSTextContainer *container = [[NSTextContainer alloc] initWithSize:CGSizeMake(0, CGFLOAT_MAX)];
    container.widthTracksTextView = YES;
    container.heightTracksTextView = YES;
    [layoutManager addTextContainer:container];
    [textStorage addLayoutManager:layoutManager];
   
    self = [super initWithFrame:CGRectZero textContainer:container];

// ...

Now, the interesting part is, specifically, heightTracksTextView = YES.

After several debugging hours, i figured out that iOS 7 was setting, by default heightTracksTextView to NO, after setting the UITextView’s scrollEnabled property.

Guess what? that’s different in iOS 8. Calling setScrollEnabled is not backfiring anymore. For some reason, if you just disable heightTracksTextView, and initialize your UITextView instance with that custom NSTextContainer, any call to the caretRectForPosition method will fail.

By that, i mean, caretRectForPosition will return an invalid position. Workaround? manually disabling heightTracksTextView, right after calling the super initialized.

By the way, for future reference, this post and this one helped me understand i wasn’t alone in Mordor.

Debugging UIKit Usage on BG Threads

Based on this awesome cocoanetics post, and updated to work with JRSwizzle, you might find the UIView+Debug category very handy, specially when debugging die hard bugs.

#import "JRSwizzle.h"

@interface UIView (Debug)

@end


@implementation UIView (Debug)

- (void)methodCalledNotFromMainQueue:(NSString *)methodName
{
    NSLog(@"-[%@ %@] being called on background queue. Break on -[UIView methodCalledNotFromMainQueue:] to find out where", NSStringFromClass([self class]), methodName);
}

- (void)_setNeedsLayout_MainQueueCheck
{
    if (![NSThread isMainThread])
    {
        [self methodCalledNotFromMainQueue:NSStringFromSelector(_cmd)];
    }
   
    // not really an endless loop, this calls the original
    [self _setNeedsLayout_MainQueueCheck];
}

- (void)_setNeedsDisplay_MainQueueCheck
{
    if (![NSThread isMainThread])
    {
        [self methodCalledNotFromMainQueue:NSStringFromSelector(_cmd)];
    }
   
    // not really an endless loop, this calls the original
    [self _setNeedsDisplay_MainQueueCheck];
}

- (void)_setNeedsDisplayInRect_MainQueueCheck:(CGRect)rect
{
    if (![NSThread isMainThread])
    {
        [self methodCalledNotFromMainQueue:NSStringFromSelector(_cmd)];
    }
   
    // not really an endless loop, this calls the original
    [self _setNeedsDisplayInRect_MainQueueCheck:rect];
}

+ (void)toggleViewMainQueueChecking
{
    NSError *error = nil;
    [UIView jr_swizzleMethod:@selector(setNeedsLayout)
                    withMethod:@selector(_setNeedsLayout_MainQueueCheck)
                         error:&error];
   
    [UIView jr_swizzleMethod:@selector(setNeedsDisplay)
                    withMethod:@selector(_setNeedsDisplay_MainQueueCheck)
                         error:&error];
   
    [UIView jr_swizzleMethod:@selector(setNeedsDisplayInRect:)
                    withMethod:@selector(_setNeedsDisplayInRect_MainQueueCheck:)
                         error:&error];
}

@end

Loading SecCertificateRef from PEM String

In order to load a PEM certificate, you’d probably wanna grab the PEM itself from your backend, right?.

You can do so, by means of this command:

openssl s_client -showcerts -host host.com -port 443

Once you’ve got the certificate, you should get rid of the Begin/End Certificate substrings.

Cocoa Snippet itself is quite easy:

NSData *rawCertificate              = [[NSData alloc] initWithBase64Encoding:PlaintextCertificateString];
SecCertificateRef parsedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)rawCertificate);

That’s it. Don’t forget about checking expiration dates. Unfortunately, Apple’s API to do so is private, and i personally refuse to build OpenSSL into my app, just to check that.