/home/embrenneke

A mostly-fresh start

Blindly Dispatching to the Main Queue

Update 9/12/2018 I’m told dispatch_get_specific does not work the way I think it does, and this is not a valid solution. Please do not use this. Leaving up for posterity.

Original: In my current side-project, I’m writing a swift app that makes liberal use of dispatch queues to move work off the main queue to keep the app responsive. This works great, but there are some Apple APIs (like UIKit) that require calls to be made from the Main thread or Main queue (they are distinct things).

I want to be able to dispatch work to the main queue synchronously, without worrying about whether I’m already running on the main queue or not.

Typically people suggest checking NSThread.isMainThread() before calling or dispatching an action, but if the api you’re calling requires the main queue this may not be enough. You can potentially be running on the main thread, but not the main queue.

There used to an API to check your current dispatch queue, but that was deprecated with iOS 6. The current strategy appears to be setting a queue specific variable that can only be read from the main queue. This is analogous to thread specific storage (TSS) in in C++ land.

I’ve wrapped this all in an extension to DispatchQueue in Swift 4.1. Now all I need to do to blindly dispatch synchronous work to the main queue is call DispatchQueue.dispatchMain { // uikit stuff }.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extension DispatchQueue {
    private static let mainQueueKey = DispatchSpecificKey<Int>()
    private static let mainQueueValue = 1234

    public class func initializeSafeDispatchMain() {
        // set a queue-specific variable that can only be read from the main queue
        DispatchQueue.main.setSpecific(key: mainQueueKey, value: mainQueueValue)
    }

    public class func dispatchMain(_ block: () -> Void) {
        // if the queue-specific variable matches, execute the block immediately
        if DispatchQueue.getSpecific(key: mainQueueKey) == mainQueueValue {
            block()
        } else {
            // queue-specific variable was not found, so dispatch to the main queue
            DispatchQueue.main.sync {
                block()
            }
        }
    }
}

The problem with this approach is initializing the queue specific variable. Right now I have it wrapped in a function called initializeSafeDisptchMain() that must be called by the application before the first use of dispatchMain(). I’d love to find a way to run this automatically on app startup without having to manaully call it from application(:didFinishLaunchingWithOptions:), but I’m blanking on it right now. If you can think of a way to do this that isn’t totally gross, please let me know.

Adding the OEM Backup Camera to a 2012 Dodge Charger

One of the biggest pain points of owning a modern Dodge Charger is that the rear visibility is basically nil. A backup camera was offered on the vehicle to compensate, but I bought mine used and the previous owner did not buy that option. Thankfully, there a couple sellers that offer the OEM part on Amazon.

The one I bought varies from $225-$350 ($228 at time of purchase), and comes with the tail light, wiring harness, and about 20 pages from the FSM describing how to install the wiring harness. On the Charger the rear view camera is built into the tail light, if equiped, so to add the camera you need to replace the whole light assembly.

New Light Assembly

I could not find any formal documentation on how to get the old light out of the trunk lid, or informal documentation that wasn’t behind a paywall or forum login, so I though I’d writing it up here with a few pictures.

Replacing a 2012 Dodge Charger Center Tail Light

The old light without a camera hole.

Old light without a camera hole

Open the trunk. Once open, you will need to remove the security box in this picture.

Security box

It is held in with a couple of small torx screws.

Torx screws

Then, each corner of the lid is covered with plastic peice. They are held in place with a plastic christmas tree. There are specialty puller tools you can buy, but I found the claw side of a hammer works just as well. Once the plug is out, the plastic cover should slide off.

Plastic Cover

Then there are 4 more christmas tree plugs holding the felt liner on that must be removed (at least on my 2012).

Felt Plug

Once they are out, the felt liner should pull right off.

Felt liner

With the lid open, should should have clear access to the center tail light.

Felt removed

It is just held on with a pair of small bolts and also clips into place.

Back of old taillight

If you look closely, you will see an extra plug not connected to anything. My 2012 was already pre-wired for the backup camera. From what I have read, most Chargers with the 8.4 Uconnect screen should already be wired, so you should not need the wiring harness that came with the new light. If yours was not factory wired, you will need to follow the FSM directions included in the package to install the included harness. Good luck with that, as it looked like there was a lot of removing interior panels involved.

Once you have removed and disconnected the original light, just connect and bolt in the new one.

New light installed

Once everything is strapped back in, replace the felt and the secured box and you should be done. The new light looks just like the old with the the addition of a camera on the left.

Closed up new light

At this point it is installed and the light and trunk release work, but the camera still does not display on the Uconnect system when I am in reverse. I need to take it to the dealer and have them add the camera option to my VIN and flash the Uconnect firmware.

Update Feb. 5, 2016

Dashboard view

I finally got the dealer and had them update the sales code for my vehicle and flash the Uconnect firmware. When in reverse, the view the from the camera fills the entire 8.4" screen. The picture quality is meh, and the bottom 1/10th is a shot of my bumper, but otherwise it works fine. I’ve finally got some rear visibility when backing up!

SSL Enabled

For no good reason whatsoever, this site, and everything else I host on this vps, now supports SSL/TLS via LetsEncrypt. We’ll see if I remember to update the cert every 90 days until they start granting longer expiration dates.

CoreData Multi-context Error

When using CoreData, it is possible to create a NSManagedObject that lives outside of a NSManagedObjectContext by passing nil as the context arguement to -[NSManagedObject initWithEntity:insertIntoManagedObjectContext:]. If you later decide you want to store the object, just use -[NSManagedObjectContext insertObject:]. This works great, unless you have parent/child contexts. If you use this method and insert into a child context then the new object attributes do not get propagated up to the parent context when saving. The resulting data loss is bad news.

You can get more details and run my sample project on GitHub.

GuestBook for iPad Removed

Developer Removed From Sale

After three and a half years of mediocre sales, I have decided to remove GuestBook from the iTunes App Store.

I briefly touched on the origins of the GuestBook app when I open sourced the app 2 years ago. The diminishing motivation caused by having a kid and the lack of a personal use case have caused me to re-evaluate even selling the product. Why not just make it free? Because there are now 18 other GuestBook- type applications in the app store search results. Some are free, some charge good money (at least for the app store), and all of them are more likely to receive an update than the original (mine).

I did eventually ship another update in July of 2014 to fit the flat aesthetics of iOS 7, and to allow users to export their data. I think that final point was the key to allowing me to finally emotionally let go of this project. I was beset with guilt over selling a product to hold people’s touching personal moments, only to potentially trap those moments forever if a future OS upgrade were to break the app. Everyone who has the app now can export those events as a web page and save them elsewhere.

I have other side projects in mind and I have started work on one that looks promising. Hopefully it won’t take me years to find the free time to finish and publish it.

Ending Irrational HULK SMASH

Laura Savino gave a wonderful talk at CocoaConf in Seattle about about getting physically angry at bad code and how that’s not a good thing. That struck me particularly hard because I too get irrationally angry when I see bad code. If I’m lucky a long walk around the track at work is enough to calm down, but more often than not it seems like my whole day is shot and I go home grumpy.

I spent a lot of time last week reflecting on why I get so angry. Half the time it’s not code that I’m ever going to see again or have to maintain. Why am I upset? I didn’t get upset at the terrible code my fellow students wrote in the CS397 capstone class in college when I was grading. What changed in the last 10 years?

For me I think my intense irrational anger came when I rotated into my last group at my previous job. Up to that point everyone I had worked with before was either good at what they do, or at least still trying to learn and improve. What changed in my last group is that I discovered a new type of programmer. Ones with don’t-give-a-fuck-itis. They typically had 20-30 years of experience with the company, and were really just trying to hold onto a paycheck until the next round of layoffs would hopefully give them a chance at early retirement. I saw the absolute crap that DGAFites produced, and it was maddening.

Because I knew their grade level and the company’s pay scales, I generally knew about what they were supposed to be making. I was furious that these people were getting paid 2x-3x more than me to make mistakes a student would point out as obvious. Or claim a new technology they were investigating isn’t panning out while I watch one sit and read slashdot all day.

I stayed in that group for nearly six years. Over time my specific rage toward those people and the system that enabled their behavior translated to anger at bad code in general, regardless of the source. This is a problem. I’m supposed to be a senior engineer now at a company that hires mostly recent grads. When you’re new, making mistakes IS learning. It’s my job to mentor them. Show them not only what is wrong when they screw up, but why it’s a mistake in a supportive way. It’s my job to make sure they don’t end up the 30 year-old burn out with anger management issues. Step one is empathising with the newbies. I need to remember what it’s like to try something new and fail miserably. AFAIK no one at my current employer is just hanging on until pension time, and even if they were the pay gap between myself and them is much smaller. Anger at bad code is completely irrational and improper, and I musn’t see its author as a bad person who should feel bad for sucking, but just another human trying to grow and do better. As Daniel Steinberg said in his opening keynote, I need to try and Be Nice.

2014 CocoaConf Seattle Recap

I just spent this weekend at CocoaConf in Seattle, and it was great. From Daniel Steinberg’s Swift Kickstart, to Gus Mueller and Chris Adamson discussing Core Image, to Paul Goracke’s CoreData tips, there was a lot of technical content I can apply directly to either my day job or my side projects. It was also neat to finally meet some people I had previously only known through their twitter avatars. But I think the real personal value I got out of CocoaConf this week were a pair of keynote-style talks from the Khan Academy’s iOS developers.

First was Andy Matuschak’s Value and State: Functional and Imperative. I have been aware of a growning functional programming community for a while, but as with any new movement the die-hard extremists killed any enthusiasm I may have had into looking into. Plus, my cave-man brain actually LIKES C and C++, and one day I may finish the half-written post about why higher-level languages are hard for me. But Andy did an excellent job of breaking down what object and value types are useful for, and why separating action from logic is an important goal. He didn’t try to convince the room that we should write everything in a functional style (you can’t write useful software that way). Instead he advocated splitting objects into a thick value layer, and a thin imperative shell to handle responding to inputs from the system and tracking the change in versions of the immutable value layer.

The night before I had an idea for a new iOS side-project, and I had planned on starting in Objective-C, and sprinkling in Swift whereever it made sense. That is not how I’m going to learn and grow. Instead, I’m starting in Swift, and I’m only going to break into Objective-C when I can’t get something done in the current version of Swift. I with the standard library being almost entirely value types (structs), it will help me design my own software to act in a more functional way, without jumping whole-hog onto the crazy train.

As an asside, Brent Simmons had spoken the night before about (not) shipping Vesper for the Mac yet, but gave an impassioned arguement on why we should be writing Mac software. So this new side project is going to be written in Siwft, on iOS and Mac.

Finally what hit me the most was Laura Savino talking about being ANGRY at bad code, and how unhealthy and damaging that is to our relationships with other people. I have much more to write about my personal struggles with code hate, so I plan on breaking that out into a separate post instead of squeezing it in here.

Beth, Sam, and I are heading out tomorrow for a little vacation this week. I had planned on bringing 2 laptops and getting started on both my new project and some work-work I didn’t finish before CococaConf. Instead I’m going to leave them both at home, delete Tweetbot and Facebook, and spend a little time in reality. If you really need to get ahold of me this week just call instead.

Developer Conferences

I just read an article on a tech news site that I generally respect which took shots at GDC for its code-heavy treatises, as compared to other industry events like E3 or PAX.

For those who haven’t been, GDC stands for Game Developers Conference. It’s explicit purpose for game and game-adjacent developers, programmers, designers, artists, to share notes, compare techniques, and get into the nitty-gritty details of game development.

If your complaint about GDC is that it’s taking a more intellectual route compared to E3 or PAX, it’s because its not for YOU, game journalist. The other events you mention exist explicitly for your purposes. Stop dumping on the one event designed for game developers to get together and talk shop.

At least developers can still buy tickets to GDC. I think this is at least in part because they have a separate ticket just for the show floor/keynote hangers on like this article’s author. WWDC has failed in this regard because they insist on bundling major product announcements with what is otherwise a perfectly civilized developer conference. I would wager that WWDC tickets would be accessible to more actual developers if Apple sold a separate keynote-only ticket, or even had the big product-announcements the week before so WWDC could also have the reputation for being a code-heavy treatises like GDC.

Demand for and Apple centric developers conference is so high that several independent conferences have sprung up over the last few years. NSConf, Úll, and Çingleton all look to be doing good work, although they are very indie focused. It’s hard to get approval and funding for these smaller non-mothership based conferences for corporate devs. These smaller conferences are also missing the extreme press scrutiny of the bigger official xDCs, but I think that’s a good thing.

Can I Buy a New Mac Pro Yet?

There has been some complaining on twitter about it being December and the the new Mac Pro still not being for sale. Since I still have the source to Is WWDC Sold Out Yet lying around, I put it to good use and created Can I Buy a New Mac Pro Yet? Update: The new Mac Pro is available, so the toy heroku instance has been deleted.

Cibanmpy is functionally the same as Iwsoy. It accepts an email address, and after confirmation adds it to a mailing list that will send one email when Apple adds a Buy button to the Mac Pro web page.

The whole app is running on free/dev tiers on Heroku, so hopefully Apple releases the new Mac Pro before too many people sign up and I have to pay mailgun to send the email.

UINavigationBar Color

In iOS 7, Apple changed the navigation bar to be semitransparent by default. In doing this, they pull down the saturation of the color you set as the barTintColor. This is a very bad thing. So bad that they fixed it with 7.0.3. That’s great moving forward, but they shipped 3 minor releases with the broken color behavior, and not all users upgrade minor point releases. What’s a developer to do?

UINavigationBar isn’t changing colors if you disable transluency, but in their infinate wisdom Apple didn’t make the transluency property available through UIAppearance, so you’d have to set it manually on every navigation bar in your app. Luckily we have one more trick up our sleave. It looks like the UINavigationBar is not changing image colors if you set an image as the background. But you don’t want to create a new image every time your designer changes their mind, so create a single pixel image in code! Put the following method into a category on UIImage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (UIImage *)imageWithColor:(UIColor *)color
{
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}

I allow my designers to specify a main theme color with a hex value, and have the following method in a Category on UIColor:

1
2
3
4
5
6
7
8
9
10
+ (UIColor *)colorFromHexString:(NSString *)hexString alpha:(float)alpha
{
    // Hex strings may optionally start with a #
    NSString *strippedString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""];
    NSScanner *scanner = [NSScanner scannerWithString:strippedString];
    unsigned int rgbValue = 0;
    [scanner scanHexInt:&rgbValue];

    return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:alpha];
}

Putting in all together, you can now set the background color of your UINavigationBar application wide using UIAppearance! I have an -[AppDelegate initializeAppearance] method I call from application:didFinishLaunchingWithOptions: which contains the following:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)initializeAppearance
{
    // .... snip other app appearance code ...

    // ...snip getting color and alpha values from config
    NSString *hexColor = @"#007646";
    float alpha = 1.0;

    UIColor *barColor = [UIColor colorFromHexString:hexColor alpha:alpha];
    UIImage *backgroundImage = [UIImage imageWithColor:barColor];
    [[UINavigationBar appearance] setBackgroundImage:backgroundImage forBarMetrics:UIBarMetricsDefault];
}

Before:

Before Image

After:

After Image

I hope this helps anyone who has to support users on iOS 7.0 - 7.0.2.