Friday, October 02, 2015

Changing UITextView's textContainer.layoutManager.delegate to your UIViewController Swaps Line Break from Word to Character Wrap

TL;DR Don’t assign UITextView.textContainer.layoutManager.delegate to your UIViewController, bad things happen. 
Versions: OS X 10.10, 10.11 Xcode 7.0.x iOS SDK 8, 9 

Some of the highlight WWDC 2013 sessions for me where those about Text Kit. That was largely because I was working at Dow Jones on The Wall Street Journal and Barron’s iOS apps which had native text layout code. It worked extremely well, but it was largely Core Text, and it wasn’t the easiest to maintain. I thought we might be able to replace a lot with Text Kit. Unfortunately I never got the chance, but I was always curious to try Text Kit’s capabilities.

Fast forward a few years and I got my change this past week. Designers handed me a screen that looks like this:

Text Kit Design

What I always remembered about Text Kit was the easy way to exclude paths from the layout for things like images and the text could flow around it.

To refresh my memory on how to do that, I did some searching and came across Ray Wenderlich’s Text Kit Tutorial, Updated with Swift. Great, read the article, downloaded the sample, and started using the UIBezierPath calculation stuff in my real project.

That’s where things went off the rails but I didn’t realize it until today. You see the tutorial had this:

let exclusionPath = timeView.curvePathWithOrigin(timeView.center)
textView.textContainer.exclusionPaths = [exclusionPath]

The method curvePathWithOrigin was calculating a round UIBezierPath because the tutorial was inserting a round graphic into the UITextView. I needed to create a rectangular UIBezierPath (see orange box in above image), but didn’t know you could do. Maybe it was the pain from a hand injury, maybe just ignorance or I forgot, but I didn’t get it.

Of course the round bezier path from the tutorial was not flowing text around the rectangle image correctly. It all seems to obvious in hindsight.

I tried many things to fix this issue. One of those was crawling down UITextView’s internal object tree and setting textContainer.layoutManager.delegate to my UIViewController instance. I usually never do things like this but again I probably wasn’t thinking very clearly.

Setting the delegate appeared to make the text flow better because character wrapping became the default, so I left the delegate assignment in. What a mistake!

When I realized how stupid I was being with UIBezierPath and used a rectangular exclusionPath, UITextView was defaulting to character wrapping instead of word wrapping.

Of course I was going through the Six Stages of Debugging:

I finally got to Stage 5 when I built up a sample line by line until I figured out setting the textContainer.layoutManager.delegate to my UIViewController instance was a really dumb idea.

Sometimes when writing code, you make bad choices and it takes a while to figure that out...

How To: Switch an Apple Watch to a new iPhone

I just got an iPhone 6s 128 GB and I remember reading an iMore article on the merits of doing a clean phone setup or restoring from a backup mentioning how to switch the Apple Watch to a new iPhone. Here’s a direct link to the Apple Support article.

This procedure is way to complicated. The iPhone setup experience doesn’t know anything about Apple Watch, which is ridiculous.

Wednesday, September 16, 2015

Inner Exception is on Apple News! Just search for "Inner Exception"

iOS 9 is out now and one of the big new features is News and Inner Exception is there!

I didn’t really expect that Apple would take a blog like this into the News pantheon, but they did and it’s live.

Turns out the old logo was so poorly designed by me, it really looked terrible in Apple News, so I made a new one.

InEx  Orange

Since I look at Menlo all day long in Xcode and this is mostly a coding blog, it seemed like the right choice.

Friday, August 21, 2015

Just discovered iTunes Connect - Manage iCloud Download Settings

When you have an app for sale and you haven’t logged into iTunes Connect for a while, you don’t often see new features added. The Manage iCloud download settings for this app in the Pricing tab is one such feature that escaped my attention…until now!

Mange iCloud Download Settings

Super handy if you need to control this more tightly.

Friday, July 31, 2015

How To Write a Swift Generic Function Based Only On Return Type

TL;DR Annotate the return variable with a type, e.x. let foo:String? = Utility.nullableValueFromKey(“identifier", dictionary: jsonDictionary)
Versions: OS X 10.10.4  Xcode 6.4 iOS SDK 8.4 

When I started working with Objective-C coming from .NET, one of language features that I missed the most was Generics. They solve a whole class of problems that are tedious and/or require way more code than without generics. When Swift was announced with Generics...

Finally...It's Done

But Generics can be hard, thinking in T for any giving problem can make you a little crazy, especially when the compiler keeps yelling at you.

In Swift 1.0, I used generic functions to help parsing JSON server responses, the functions worked, but they were less than ideal.

The typical problem I wanted to solve was getting a primitive type out of the response dictionary that could be null.

I ended up with this function to do the trick:

class func originalNullableValue<T>(valueType: T, key: String, dictionary: NSDictionary) -> T? {
    var value:T? = nil
    var valueTemp = dictionary[key] as AnyObject! as? T
    if valueTemp != nil {
        value = valueTemp!
    }
    return value
}

Ugly! Why did I end up with this? Either I wasn’t smart enough to figure this out or the 1.0 compiler wasn’t.

Smart, but not Smart Enough

Getting either me or the compiler to figure out what type T was without passing an argument of that type into the method, was, well let’s just say it was the solution I found.

What I wanted was this:

class func nullableValueFromKey<T>(key: String, dictionary: NSDictionary) -> T? {
var value:T? = nil
    var valueTemp = dictionary[key] as AnyObject! as? T
    if valueTemp != nil {
        value = valueTemp!
    }
 
    return value
}

So I dusted off the original method and tried making it what I wanted with Xcode 6.4 & Swift 1.2.

Defining it works fine, but If you attempt to call it:

let foo = JSONUtility.nullableArrayFromKey("fooBar", json: Dictionary<String, AnyObject>())

The compiler returns this error:

Argument for generic parameter 'T' could not be inferred

I have no way to know/test if this is the same error that caused me trouble in Swift 1.0, but this time, either I or the compiler were smart enough to figure it out!

All you have to do is add the type to the variable declaration:

let bar:String? = JSONUtility.nullableValueFromKey("name", dictionary: Dictionary<String, AnyObject>())

Flawless Victory

Thursday, July 30, 2015

No Dan Gillmor, Government Should Do Nothing About Android Security

I nearly burst from laughter after reading this tweet and then article by Dan Gillmor:

What’s so funny? The free market is working in this case exactly as intended. A company in the market, e.x Apple, provides mobile devices that are usually secure and updated. Some consumers have voted with their dollars that isn’t as important to them as other criteria, so they bought an Android phone. There is no surprise here that if you buy an Android device, you highly likely will not get updates of any kind, security or otherwise. 

What criteria stops people from buying an Apple device? Let’s return to Mr. Gillmor:

Apple's iOS devices, of course, are part of a tightly controlled ecosystem, and while Apple is far from perfect on security, it does update iPhones. But we shouldn't be required to turn over our computing and communications to control-freak companies in order to get necessary security updates.

So let me get this straight? Mr. Gillmor doesn’t want Apple devices because Apple is a “control-freak” company, so he invites the control-freak government to use laws &  regulations & force Android implementors to be more control freaks about updates…like Apple. LOL. Sure, the government is always the lightest touch!

If Android users thought updates and security where higher priorities than cheap phones or “open source” software, then they wouldn’t have bought an Android device.

Returning this as not a bug, working as intended!

Epilogue

Google made this mess, they can still fix it. They already offer an Android Bug Bounty. They have a generic Patch Reward Program.

Instead of the heavy hand of laws and regulations, Google should start an Update Rewards program.

Every carrier or vendor that releases Android updates in a timely fashion (say within 1 month) gets a payment from Google.

Security Updates pay more than Feature Updates. To really sweeten the pot, Google can pay per user upgraded, get some vendor/carrier incentive to update as many users as possible.

Vendors/carriers have such thin margins, seems like they don’t have the money to cover testing and deploying Android updates without taking a loss.

Use some of that ad revenue to cover the costs. I mean, advertisers should be clamoring for this. After all, how can they trust the ad profiles Google vends with compromised devices?

Monday, July 13, 2015

How to Completely Eliminate UITableView Content and Separator Indent on UITableVIewCell

TL;DR

overridefunc viewDidLoad() {

    super.viewDidLoad()

         

    self.tableView.separatorInset = UIEdgeInsetsZero

    self.tableView.layoutMargins = UIEdgeInsetsZero

}

 

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    cell.layoutMargins = UIEdgeInsetsZero

}

Versions: OS X 10.10.4 Xcode 6.4 iOS SDK 8.4

Almost every iOS app has at least one and usually more UITableViews and associated UIViewController or UITableViewControllers to manage them.

The view’s ubiquity has to make implementing it very tough and I don’t envy the team that has to account for all the use cases.

Over time, UITableView, UITableViewDelegate, UITableViewDataSource, and UITableViewCell have become very large. Throw in their subclasses, UIScrollView & UIView, and developers have to remember a lot of stuff to get all the behavior they want.

Or in this case, don’t want. UITableView enforces default layout margins and separator margins that are not obvious…once you use AutoLayout.

This is what you see in your storyboard:

UITableView Storyboard

 

This is what you see in the app:

UITableView Insets

Notice the left hand margin? There is no option you can change in the Storyboard to fix it, you just get an indent/margin/inset you didn’t ask for. The storyboard doesn’t show this, so I’m not sure if this is a bug in the Storyboard editor, or in runtime in UITableView.

What’s happened is that I added a few AutoLayout constraints to the left red bar (margins t: -8, l: -8, b: -9).

When I do this, the tableView.layoutMargins appear to be their default value of 8. It looks like without AutoLayout, UITableView resets layoutMargins to UIEdgeInsetsZero, but with AutoLayout, it either wants the default margins or there’s a bug.

You have to add the following code to fix the pre-cell tableView display:

override func viewDidLoad() {

    super.viewDidLoad()

         

    self.tableView.separatorInset = UIEdgeInsetsZero

    self.tableView.layoutMargins = UIEdgeInsetsZero

But that’s not enough to get the cells looking right. Based in part on this StackOverflow post, but with my own testing, you have to implement the delegate callback willDisplayCell like this:

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    cell.layoutMargins = UIEdgeInsetsZero

}

Why do we have to set layoutMargins again in willDisplayCell? Apparently the cell’s layoutMargins get reset again right UITableView decides where to draw the separator line, so you have to make them whatever you want. Curiously, willDisplayCell has existed since iOS 2, but I don’t remember needing to use it before iOS 8.

Now we get what we want.

UITableView Insets fixed

Another Workaround That’s Incomplete...

This other StackOverflow post mentions the UIView property preservesSuperviewLayoutMargins. It defaults to NO/false, but for a UITableViewCell is set to YES/true.

You could have mostly solved the problem by explicitly setting that property to NO/false, but it would not have fixed the separator issue.

iOS 9 Beta 

I haven’t run through this on iOS 9 beta, so I don’t know what the the default indent situation is there.

Friday, July 10, 2015

Change the Global Tint in an Xcode Storyboard

TL;DR Look at the File Inspector tab (the one with the blank document) of your storyboard

Whenever I go looking for the Global Tint defined in the main storyboard in Xcode, there’s always a moment of cognitive dissonance.

My problem is that I go looking for some kind of Window object, which of course is how you’d do it programmatically (in Swift):

UIApplication.sharedApplication().delegate?.window!!.tintColor = UIColor.redColor()

The double !! after window is not a typo according to Xcode, though it sure looks weird.

Anyway, I guess nobody wants to make UIWindows things in Storyboards, so this property is stashed under the File Inspector tab!

Friday, June 12, 2015

GitHub is Not Your Resume

Your resume is your resume. 

Your released apps are your resume. 

Your released sites are your resume. 

Your published blog posts are your resume.

Your contributions to widely used open source software, some of which might be hosted on GitHub, are your resume. 

Your public repo that you're the sole contributor to and isn't a shipping product is not your resume

Hiring managers screening applicants don't have time to pull 3+ repos, attempt to compile them, and then read the source to see what a 10x dev/rockstar/ninja/special snowflake you are.
 

Thursday, May 14, 2015

Thursday, May 07, 2015

Cocoapods use_frameworks! Means A Bridging Header Not Required for Objective-C Pods in Swift

TL;DR Use standard "Import Framework" in Swift instead of the Obj-C bridging header for Obj-C Cocoapods with use_frameworks! in your Pods file
Versions: OS X  10.10.3 Xcode 6.3.1 iOS SDK 8.3  Cocoapods 0.36.1

This just wasn't obvious at all. I've been using Swift & Cocoapods < 0.36 since Xcode 6 shipped. While versions < 0.36 did not work with a Swift framework like Alamofire, you could use Objective-C pods and let them get compiled as a static library as long as you used the Objective-C bridging header to import the header(s).

With Cocoapods 0.36 and above, you can have Swift frameworks as Pods. To make that work, you have to put the use_frameworks! keyword in your Pods file. My brain misunderstood Use frameworks instead of static libraries for Pods because it automatically inserted for Swift before the Pods in that sentence. 

Took me a couple hours this morning to work out all my Pods might be framworks. I'm not the first person to figure it out, just a few days ago, Johannes Luderschmidt published a blog post with the solution which put on the right path. All Pods using use_frameworks! are frameworks, not just the Swift pods. A problem like this is where experience with Xcode can really work against you. You start tweaking Header Search Paths,  Framework Search Paths, User Header Search Paths, and the Always Search User Paths (docs: disabling it is strongly recommended) settings in desperation because that's usually where these problems lie. Especially when the first compiler error in your project after putting use_frameworks! in your Pod file is that the header for come Objective-C pod can’t be found.

But that’s totally down the wrong rabbit hole. I’m using the Obj-C MBProgressHUD Pod. What you need to do is:

  1. Remove the #imports from your bridging header
  2. Add Import MBProgressHUD into every Swift file that needs the class.
  3. Fix the enums

You're now using bona fide frameworks, so your enums have moved in flight! You might have a line of Swift that was fine with the bridging header like this:

progressHUD.mode = MBProgressHUDModeIndeterminate

That now has to become this:

progressHUD.mode = MBProgressHUDMode.Indeterminate

Not to big a deal, but the pile of errors might lead you astray that you have a bigger problem than you do if you’re using a lot of Obj-C enums.

Wednesday, April 22, 2015

Deleted my Instapaper Account, Here are Some Stats, and Why the Service Didn't Work For Me

TL;DR 645 Unread 25 Archived 1 Starred

I've had an Instapaper account since 2008 when Marco Arment launched the service until now. I haven’t been using the account for most of the last 2 years, with my last save in June 2014, the only one the whole year.

So I deleted my account. Finally!

The idea never really worked for me, even though I was very excited about it. I’ve always been a news junkie. My routine is daily RSS reader, hourly Twitter timeline, Facebook News Feed mostly on weekends, and daily manual reading of a number of sites. The idea of a service that could generate a newspaper like thing for me to read later seemed great. But I realized that after I went through my usual news reading routine, my attention was exhausted. 

Instead, I used Instapaper as a bookmarking service for “important stuff”, longer form deeper think pieces I thought I absolutely had to know and would get to later. That also turned out to be untrue. Out of the 671 links I added to Instapaper, how many did I actually read? 26! 3.8%. That’s way lower than even I expected.

I’ve been using the Reading List feature built-in to Safari on iOS and OS X, but it’s where links kind of go to die for the same reasons as Instapaper, I just don’t go there, does’t fit into my routine. Bookmarks are a similar story, only the Bookmarks on my favorites bar see any action.

What I think I might need is something to save links grouped by research topic, not just one big list. Bookmark Folders kind of serve that purpose, but it’s not really an elegant or searchable tool.

I’ve also kind of become one of those people on a desktop browser that just leaves a browser window open per topic with tabs for each site until I’m ready to deal with it. Safari does an excellent job of keeping this state at the ready in my Dock on OS X.

 

Wednesday, April 08, 2015

How To Use the Spock Live Long and Prosper Emoji on iOS 8.3 & OS X 10.10.3

Versions: OS X  10.10.3   iOS 8.3
 
Despite the date, this article by Jason Snell at Six Colors about the Spock Live Long and Prosper hand gesture, referred to as Raised Hand with Part Between Middle and Ring Finger, is not an April Fool’s Joke.
 
iOS 8.3 and OS X 10.10.3 can display the emoji, but it isn’t in the standard Emoji keyboard on either OS yet.
 
To be able to use the emoji on OS X, you can create a text replacement macro to use it now:
  1. Copy the emoji from here: Raised Hand with Part Between Middle and Ring Finger (Blogger keeps double-encoding the emoji and makes it unreadable).
  2. Go to System Preferences -> Keyboard -> Text tab.
  3. Click +.
  4. I used LLAP in the Replace column and the emoji in the With column.
  5. That’s it!
Now when you type LLAP on OS X, you’ll get prompted to use the emoji.
 
Now on iOS:
  1. Copy the emoji from here: Raised Hand with Part Between Middle and Ring Finger
  2. Go to Settings -> General -> Keyboard -> Shortcuts.
  3. Tap +.
  4. Paste the emoji into Phrase.
  5. Type LLAP into Shortcut.
  6. Tap Save.
Now when you type LLAP on iOS, you’ll get prompted to use the emoji.
 
Live Long and Prosper!

Friday, April 03, 2015

With Autocompleted Methods & Functions, I Wish Returned Values Came After the Call In Swift

We’ve been calling functions or methods on objects the same since at least C appeared in 1972. You declare & call a function like this:

int main(void) {
    printf("hello, world\n");
    return 0;
}
int value = main();

To define, the return type is declared before the name of the function. On call, a variable is declared to hold the return value of the function call.

In Swift, you declare a function with a return type like this:

  • func printAndCount(stringToPrint: String) -> Int {

        println(stringToPrint)

        return countElements(stringToPrint)

    }

I loved this change to the C convention. IMHO, the information importance follows the order you declare the function: name, parameter type(s), and return type(s). However, you still call that function like this:

  • let count = printAndCount("Hello, playground")

This feels like a job half done, a little bit like the tail wagging the dog

I nearly always know the name of a function or method I want to call before I know its return types.

Wouldn’t it be great if the language was fully adapted to the pervasiveness of autocomplete?

Imagine the amount of time you’d save if you could start typing the name of the function or method first and let autocomplete show & then fill in the return types?

Proposing Post Call Return Value Assignment

I’m proposing that variables that are assigned the return value of a function should come after the function call using a return arrow. It would look something like this:

printAndCount("Hello, playground") -> let count

With methods, it would look like this:

someObject.printAndCount("Hello, playground") -> let count

The actual syntax is up for debate, but I’m excited by the idea. I don’t know of another language that does this.

Pros

  • Far fewer trips to documentation to lookup the return types of a function you know the beginning of.
  • Far less typing of return types of any kind
  • Far less (no?) typing the object, autocompleting the method, then fixing up the return types to variables you want.

Cons

  • Return variables harder to “find” when reading code
    • Listing for completeness, I don’t see it being materially wrong, just different from the norm
  • Imagine the style debates because the existing way stays in the language.

I’m betting the pros would outweigh any cons, and this feature would greatly increase the usability of the language and editor together.

Thursday, April 02, 2015

iOS MPMoviePlayerController Crashes when prepareToPlay is Called Without Adding to a View

TL;DR Call something like [self.view addSubView:moviePlayer.view] before [moviePlayer prepareToPlay]
Versions: OS X  10.10.2 Xcode 6.2 iOS SDK 8.2 

I just inherited some code previously compiled against iOS SDK 6.0, ran fine through iOS 7, but crashes on iOS 8.x. Here’s the crasher:

NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"impact_of_needs_goal_planning@1x" ofType:@"mp4"]];
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
self.moviePlayer.shouldAutoplay = NO;
[self.moviePlayer setControlStyle:MPMovieControlStyleNone];
[self.moviePlayer.view setFrame:CGRectMake(0.0, 0.0, 1024.0, 748.0)];
[self.moviePlayer.view setTag:7];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieDidLoad:) name:MPMoviePlayerLoadStateDidChangeNotification object:self.moviePlayer];
[self.moviePlayer prepareToPlay];

App was crashing on the last line, [self.moviePlayer prepareToPlay]; every time it was called.

This is a guess (because installing an iOS 7 simulator is taking forever), but it looks like iOS 7 and below didn't care because the app was working fine. iOS 8 crashes.

The solution is adding the moviePlayerController to a parent view before calling [self.moviePlayer prepareToPlay];, like this:
[self addSubview:self.moviePlayer.view];

Given this is The Right Thing To Do™, I'm actually surprised it ever worked!

Update

Turns out this was not the cause of the crash, which I have yet to nail down. This change did seem to temporarily solve the crash, but alas it is something to do with NSNotifications :’(

Thursday, March 12, 2015

Reminder: iOS's UICollectionView Added an Item Property to NSIndexPath

TL;DR Use the item property on NSIndexPath for UICollectionView instead of row from UITableView 

Just realized I littered a UICollectionViewController with calls to indexPath.row when I should have been using indexPath.item. I didn’t see any bad behavior, but should use the right property for the job!

Reference:

NSIndexPath UIKit Additions (Web)

Thursday, March 05, 2015

Workaround for iOS Simulator Not Sending Current Location to Your App

TL;DR Click the Debug -> Location -> Apple menu.
Versions: OS X 10.10.2 Xcode 6.1.1 iOS SDK 8.0 iOS Simulator 8.1 (550.3)
iOS Simulator Debug Location MenuI often reset the iOS Simulator to quickly wipe out installed apps, settings, keychain items, etc. After a reset maneuver, the simulator often refuses to deliver the the users current location. I usually have a Custom Location (as pictured in the screenshot to the right) configured to get back to the same place every run.
When the simulator gets stuck, a quick workaround is to change the location to a pre-defined one, e.g. Apple. It kind of feels like Han Solo smacking the Millennium Falcon to get it to power on in Empire Strikes Back!
Just click the Debug -> Location -> Apple menu.

Wednesday, March 04, 2015

Reason Why Your Storyboard Defined UICollectionViewCells Are Blank on Run

TL;DR Remove the / do not add a call to... self.collectionView.registerClass in viewDidLoad
Versions: OS X 10.10.2 Xcode 6.1.1 iOS SDK 8.0
Blank Collection ViewWhen you run your app to see your collection view, does it look like this? 
This isn’t your first rodeo, you’ve implemented UICollectionView before, but maybe it’s been a while.
You want to do it The Right Way™, using all the modern stuff like Storyboards, and then this happens!
What's gone wrong?
You have what seems like a reasonable line of code in viewDidLoad that UICollectionView is choking on!

How You Could Have Got Here...

Let’s say you created a UICollectionViewController in a Storyboard or a UICollectionView in a plain old UIViewController. You’re confident, not cocky, but you know Apple’s pattern on this pretty well, so you go ahead and customize the first cell, nothing that requires code, and then add a second cell that you expect to set properties on, so it might look like this:
Screen Shot 2015 02 27 at 7 44 19 AM

Best Way To Add Code for Storyboard?
Back in the day when Interface Builder was a separate app (pre Xcode 4), there was an option to auto generate class files right from within the UI. If memory serves, this would create the class file as mostly what you wanted and connected to IBActions and IBOutlets.
Since Xcode 4, you can drag actions and outlet into your source code, but the source files have to already exist. If you want to create source, the only way I can find is from the process on the left. There's some typing & option choosing which are needless because you already made those choices in your Storyboard. Worse, the source is not customized in any way from the changes you already made in the Storyboard.
Anyone have a less manual way? Let me know, I can't seem to find one.
Now you think it’s time to add code for the view controller you created in the storyboard, so you go through the File -> New -> File (or ⌘N if you do this a lot) dialogs like in the screenshots below: (your mileage may vary with different Xcode versions)
 Screen Shot 2015 02 27 at 8 00 03 AM Screen Shot 2015 02 27 at 7 56 17 AM

 

The code template that's used for a UICollectionViewController seems like a reasonable starting point. It adds the following line to viewDidLoad:
Obj-C [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
Swift self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)


If you've customized the stock UICollectionViewCell in your Storyboard, the cell with the matching reuse identifier in code will cause UICollectionView to load an empty UICollectionViewCell! If you went full steam ahead and added a bunch of custom cells with matching reuse identifiers and registerCell calls in viewDidLoad, you, ah, might not figure this one out for a while.

Tuesday, March 03, 2015

A JS Framework On Every Table

No article or blog post has ever captured how I feel about Javascript like a JS framework on every table.

It’s not like I hate Javascript, but with limited time to invest in learning stuff outside the Apple stack, the constant turnover in JS frameworks means nothing ever really rises above the level of noise for me.

Except Angular.js! Looked like that had some heat, some staying power. Too bad Angular is breaking compatibility between 1.3 and 2.0. I’d heard many good things and even dabbled with it on a project, and thought it might be time to dive deeper, but now…*sigh*

Thursday, February 19, 2015

2014: The Year Video Games Broke My Heart Part 2 - Destiny

Destiny: Be Mediocore

In 2014: The Year Video Games Broke My Heart Part 1 I detailed how the quality deficit in Halo: The Master Chief Collection needs to lead to a change in behavior: no more pre-orders and no more day 1 purchases.

In 2014: The Year Video Games Broke My Heart Part 2 - Game Review Sites Strike Back! shows how a couple game review sites hopefully lead the charge to reviewing the product and experience retail game buyers have before rating a game, which should act as a deterrent to shipping shoddy products to retail.

But flat out broken games weren’t the only huge disappointment in 2014. The most anticipated & hyped game of 2014, Bungie’s Destiny, wasn’t even close to living up to expectations.

What went wrong with Destiny? Destiny is like the best C+ shooter you’ve ever seen. First, here’s what went right:

Before I unload the complaints, I have to tell you dear reader that at the time of this writing I’ve played Destiny for 3 days, 11 hours, 35 minutes! That’s just the shipping game, I also poured a bunch of undocumented hours into the beta. I’m not even a hardcore player, there are tons of people with hundreds of hours played in this game!

What could be so wrong with a game that millions of gamers and I around the world have spent so much time in? A game profitable on day 1? It’s complicated, but even Destiny’s most ardent fans (largely) agree on this:

  • A story that barely makes any sense in game
  • A ton of story shunted off into something called a Grimoire, which is a collection of "cards” to read on a website or app.
  • Laughably bad dialog from a lot of the in-game voice acting (e.g. "I don’t have time to explain what I don’t have time to explain")
  • A bewildering number of currencies and materials (glimmer, vanguard marks, crucible marks, motes of light, strange coins, black wax idols, ascendent shards, ascendent energy, exotic shards, I might have missed something) 
  • A bewildering number of factions to grind reputation for (Vanguard, Crucible, Dead Orbit, New Monarchy, Future War Cult, Iron Banner, Queen, Eris Morn, Cryptarch)
  • Factions (New Monarchy, Dead Orbit, Future War Cult) with hardly any back story or purpose, they’re just vendors of slightly different versions of weapons and armor
  • No Looking For Group tools in game for the toughest challenges, it’s all manual.
  • Notice I didn’t say Matchmaking, which is in the game, but not for the hardest content, and Bungie has “no plans” to do so. (Thankfully , Weekly Strikes Will Soon Be Matchmade).

And this is just for starters. I mean there are so many things wrong, incomprehensibly nonsensical, it truly boggles the mind. Players have spent tons of time on the Bungie community forums documenting this and much, much more…the threads live for a while, then disappear either intentionally or they lose all heat and get auto-scrubbed.

This is a great look at why the game was such a letdown at launch and largely still to this day:

 

Yet Million Keep Playing…or Are We?

For all the complaints, a lot of people are hooked, digging deep to rationalize their addiction. For me the continued playtime is twofold. I’ve already paid for more content then I’ve received and I remain hopeful Bungie can pull this thing out. What do I mean? I bought the $90 Digital Guardian Edition, which included The Dark Below and House of Wolves expansions. The Dark Below was not an improvement, but more of the same. Still I enjoyed some of the changes, and the missions, while still not making a ton of sense, were decent.

The big hook though is winning that sweet loot. Chasing the carrot (even while there aren’t enough), pulling the slot machine handle one more time, or this time getting the winning lottery ticket. What do I mean? Every reward in the game is handed out at random. Materials, armor, and weapons. There are few guaranteed paths to acquiring everything, and the ones that do exist (Exotic Weapon Bounties) are extremely time consuming. The tantalizing possibility of winning by chance (not earning) loot is the hook that keeps people doing the same repetitive tasks over and over again. Playing the same mission, strike or raid (in ascending order of time & difficulty and thus possible payout) is no different from going to a casino for hours and hunkering down with a bucket of coins and pulling the slot machine handle or tapping spin over and over again.

This is perhaps the biggest disappointment of all. It feels like Bungie went full dark side, embracing every dark pattern used first by the gambling industry and increasingly the video game industry to keep us playing, and playing, and playing to get the loot, to beat the monsters, to get the loot, to beat the monster, ad nauseam.

Yet with all those tricks in full use, hardcore players are hitting a wall while waiting for the The House of Wolves expansion, which is coming out later than many expected. I’m not hardcore enough, I haven’t had enough time, to exhaust the raid content because without matchmatching or in game looking for group (good for you if you like DestinyLFG.net), it’s often too much work for me to put a raid group together. Even with the great DadsOfDestiny clan on my friends list, the game doesn’t help people on your friends list figure out which activities you’d like to do, it’s all website forums or Xbox One (for me) messages.

Yes the addiction is strong, abetted by the promise I can play my characters for a long time, so how could I have avoided this love/hate relationship? Don’t believe the hype.

Call to Action

It happens to all of us from time to time, the hype gets to us, we believe, and spend money before a product is shipped. But with games increasingly the prescription is don’t fork over your money ahead of the game actually shipping (Kickstarter included). Don’t buy expansions before you’ve played the game, you’ll save yourself money, and this stuff is often on sale if the game is good. Wait for reviews of even the most hyped games before plunking down your own sweet loot.

I would not be playing Destiny if I hadn’t already paid $90 for the full game. 

Tuesday, February 17, 2015

How To Delete Conversations in Messages OS X Without a Confirmation Prompt

I get a lot of iMessages and SMSes that are informational, like transit alerts, that I just want to delete after reading them.

With the Continuity features of OS X 10.10 Yosemite and iOS 8, I end up deleting these conversations on all the devices I can get them, which is not really what you want. 

In Messages on OS X, you delete conversations by clicking the little x next to the conversation or typing ⌘⌫ (command-delete). Then you get this prompt:

OS X Messages Delete Prompt

While it would be great if Apple automatically deleted them on all devices when deleted on one device, we’re not there yet, but you can eliminate the prompt!

Just option-click the x next to the conversation or type ⌘⌥⌫ (command-option-delete)

I’m still surprised and delighted when something I want to be able to do is tucked behind an ⌥ option key press. ⌥ option click all the things!

Wednesday, February 11, 2015

2014: The Year Video Games Broke My Heart Part 2 - Game Review Sites Strike Back!

In 2014: The Year Video Games Broke My Heart Part 1 I detailed how the quality deficit in Halo: The Master Chief Collection needs to lead to a change in behavior: no more pre-orders and no more day 1 purchases.

In the last few days, two game review sites demonstrated real ethics in game journalism (more on Gamergate’s fake kind later). They've changed review practices partly or wholly in response to game quality levels and the previously controlled conditions reviews were conducted under.

Polygon has adopted a system called Provisional Reviews, which I think is fantastic. In the Halo Master Chief Collection case, this would have meant that as soon as launch day and beyond issues became apparent, Halo MCC would not have been issued a high final of 95 vended out to Metacritic (cmd-F/ctrl-F for Polygon):

Screen Shot 2015 02 10 at 6 27 00 PM

 

 

The update on Polygon’s Halo MCC review 5 days after launch from an 8 from 9.5 didn’t affect Metacritic at all. With Provisional Reviews, Metacritic would have most likely got the low quality adjusted review score. This will directly affect game studio and publisher bonus structures since studios are often awarded for 9+ Metacritic scoring game.

Eurogamer.net has dropped review scores entirely and will not submit to Metacritic. Most interestingly, they are taking steps to guarantee they are reviewing paying retail customer experiences as well:

We are also changing (or firming up) other areas of our reviews policy, with the intention of ensuring that we always review the same experience that you get when you buy a game. This means that we will only review from final retail versions and online games will be reviewed after they've launched.

Eurogamer will issue first impressions of games on launch day, but also not use their new system to issue a near immutable final recommendation until they can asses the game under real world conditions. Fantastic!

Hopefully other game review sites follow these two sites lead in:

  • Don’t issue final scores based on debug build games, only retail copies
  • Don’t issue final scores based on controlled network conditions for multiplayer heavy titles, only on 
  • Don’t issue final scores to Metacritic (if at all) until a games total experience can be judged at launch under real work usage
If enough review sites adopt these kinds of policies, then hopefully they will serve as a deterrent to game studios and publishers shipping broken products to retail.

Part 3 Coming Soon: Destiny - Be Mediocre

Friday, January 30, 2015

2014: The Year Video Games Broke My Heart Part 1 - Halo: The Master Chief Collection

Halo: The Master Chief Collection for Xbox One was the final straw, the game that broke my heart, and confirmed 2014 as an extremely disappointing year in video games. Halo was the series I fell in love with way back when the Xbox Classic came out. I pre-orderded Halo: The Master Chief Collection as soon as it was announced. I fully expected the same high quality game that 343 Industries, the studio now in charge of all things Halo for Microsoft, delivered with Halo 4. But was I and every other sucker that bought this game so wrong. What we got on release day was a beta, no other way to describe it. Fatally, multiplayer matchmaking just flat out didn’t work.

I’m done paying to be a beta tester and I’m not pre-ordering anymore.

Just Keep Patching

Halo: MCC was released on November 11, 2014. Matchmaking was completely broken. I wasn’t able to play a single multiplayer match until December 7, 2014, only just barely. Single player was more playable, but the forums and patch notes where filled with people encountering numerous bugs. Just look at the number of patches released and apology blog posts:

  1. November 14, 2014
  2. November 20, 2014
  3. November 24, 2014 - Apology Post from 343 Studio Head Bonnie Ross
  4. November 26, 2014
  5. December 3, 2014
  6. December 7, 2014 - Multiplayer matchmatching sort of works
  7. December 15, 2014 - Multiplayer is usable
  8. December 19, 2014 - Reparations as Thank Yous from 343 Studio Head Bonnie Ross & FAQ
  9. December 22, 2014
  10. January 19, 2015

8 patches and 2 apologies, one with “don’t sue us” make goods:

  • 1 free month of Xbox Live Gold (which I have received)
  • Halo: ODST Ported to Xbox One, upgraded to HD, and added to Halo: MCC for “free” sometime in 2015

The New Normal?

Polygon called attention to the issue of broken games that ship in the opinion piece Broken video games are the new norm, what developers need to do to fix that.

From the article, here’s the money quote on games that shipped significantly broken:

That includes games like Halo: The Master Chief CollectionDriveClub and Dragon Age: Inquisition. Even games that launched relatively smoothly, like DestinyGrand Theft Auto 5 and Call of Duty: Advanced Warfare, ended up with early patches to fix problems big and small.

I’d add to this list Alien: Isolation whose forums where loaded with complaints of game breaking bugs and required a number of large patches.

In October 2013, Batman: Arkham Origins had so many game breaking bugs I couldn’t play the game without risking losing all progress. Those weren’t addressed until early 2014, and the studio publicly said it wouldn’t produce any more patches to focus on DLC! See the Technical Issues section of its Wikipedia article.

With Arkham Origins, shipping a really broken game seemed like an outlier. But the quality problems of 2014 makes it pretty clear that game studios and publishers have decided there’s no penalty for shipping broken games.

They’ll already know to some degree how much they are going to get paid through pre-orders, they know a large part of the audience for AAA is going to buy on opening day, and they control the time and conditions under which sites get to review the game.

Halo: The Master Chief Collection was initially reviewed by Polygon as a 9.5, which I’d say is what most people expected based on the package. It wasn’t until 5 days later that the game was re-reviewed to an 8.0, which I’d say is overly generous because of this quote:

The Master Chief Collection's campaign element appears to be fully functional, so players aren't totally locked out of the experience the game is offering.

In practice, there were so many things wrong with the various campaigns, many players would be wasting time to play the game at that point. Checkpoints worked, except when they didn’t, losing players progress.

Screen Shot 2015 01 23 at 8 20 53 AM

None of this means Microsoft & 343 didn’t want to ship a higher quality game, but with Day 1 & beyond patches accepted by players with complaints but no change in buying behavior, obviously the decision to ship was made and to sort out the bugs later.

The only way shipping wildly broken games doesn’t become the new normal: you have to hit them in the wallet!

No more pre-orders. No more Day 1 purchases. If enough gamers change, developers and their publishers will ship higher quality games on Day 1.

Ironic that Halo: MCC was looked at by many to embarrass Bungie with largely high quality port of their previous defining work when Destiny, the most hyped game of 2014, shipped to resounding bafflement that one of the best developers in the business could make a game so…average.

Next: Game Review Sites Strike Back!

Thursday, January 29, 2015

Apple Header Humor: I/O error (bummers)

In iOS 8.1 SDK’s Security.Framework SecBase.h, the constant errSecIO has this comment:

/*I/O error (bummers)*/

This amused me because an I/O error is always a bummer.

Apple Bug Reporting Tip: Always Capture a Sysdiagnose

While testing the 10.10.2 beta, I experienced a weird condition in Finder where commands in other apps like Show in Finder or Reveal in Finder would be ignored without error! I filed a bug report with Apple, and Developer Relations send back that it needed more information with this request:

Is this reproducible? If so, next time this happens, please take a sysdiagnose while the issue is happening. As soon as you experience the issue, press cntrl-opt-shift-cmd-period (⇧⌃⌘⌥-period)  (preferably with the finder in the foreground) and a sysdiagnose will be taken immediately (it can take a minute or two to complete). The sysdiagnose file should be revealed in a finder window. If you have to reboot, navigate to /private/var/tmp

That’s mostly boilerplate, but notice the preferably with finder in the foreground is specific to my issues.

I haven’t been able to reproduce this issue on 10.10.2 final yet, but the point is:

Always capture a sysdiagnose when weird stuff is happening in OS X that you want to report a bug on.

Thursday, January 08, 2015

Apple You Might Catch More OS X bugs With Honey Than Vinegar

I just installed the 10.10.2 beta and was excited to see Feedback Assistant added to my Dock. I quickly attempted to login with my Apple ID only to see this:

Screen Shot 2015 01 08 at 9 37 30 AM

 

I’m not sure what politics are likely involved in Apple Developers not being able to use this seemingly handy tool, but I’m sure the number of bugs reported from outside Apple would increase if an easy to use app was available to all.