The Web Ascendant

The following story is a work of fiction. The setting is entirely hypothetical, and the opinions expressed by the characters do not reflect opinions held by the author.

Five to ten years in the future.

Rob strolled into the office at 8:35 AM to find a bleary-eyed Sai sitting in front of his computer, muttering expletives under his breath.

"Morning," yawned Rob, as he took a seat at the desk next to Sai's.

"Good morning," Sai grunted, not taking his eyes off the screen. Rob peeked over Sai's shoulder to find him issuing terminal commands, rooting around in Chrome's local storage folder.

"What's up?" Rob asked, a hint of concern in his voice. Whatever it was, it couldn't be good if his lead developer was mucking with the terminal.

"I got hit with a nasty popunder." Sai said, running his hands through what was left of his hair. "And it kept spawning new windows and alerts so quickly that I had to force-quit Chrome just to make it stop. I haven't seen popups this bad since before popup blockers. Problem is, every time I launch Chrome, it re-opens that window and the process starts over again, so I'm trying to retrieve my source files before blowing away Chrome's local storage and starting fresh."

"That sucks man. Remind me never to browse the Web with the browser that runs my IDE. You know, if they ever let me escape this hellhole called middle management."

"That's the best part, I wasn't. I was browsing in Firefox, and the popunder injected code into Chrome to spawn itself there."

"For real? That must be some exploit."

"Not even. Support for Web-IPC landed in Firefox last week, and it's been in Chrome for a couple of months now. Firefox just sent Chrome some Javascript to execute, and Chrome happily went and did it! I've seen some stupid Web standards, but this one takes the cake. It's not enough that my browser automatically executes any code it comes across when I surf the Web. No, now all my other browsers have to execute that code too. Process isolation, what's that?"

"You know," Rob interrupted, before Sai could spew any more clichés, "maybe you shouldn't browse those kinds of sites at work."

"What kinds of sites?" Marie asked, returning to her desk with a full mug of coffee.

"It was just Stack Overflow." Sai protested. "And none of your suggestions so far have been helpful."

"Fair enough," Rob said. "Shouldn't the files all be backed up on AWS?"

"You would think so, but there was a massive BGP attack earlier," Sai explained. "Worst case, the files are now stored on the attacker's server."

"That might not be so bad," Marie joked. "Maybe we could ask the attacker for our files back."

"No need," Sai replied, still typing furiously on his keyboard. "I think I've pulled out everything–Hey, check it out, one of these popups contains a Bitcoin miner. That takes me back."

"What's Bitcoin?" Marie asked.

"It was the first cryptocurrency," Sai answered. "When you were busy mining redstone, Libertarians were mining Bitcoin to buy drugs and take out hits on people over the Internet. It's completely worthless these days though."

"I'd have retired by now if it wasn't for the Bitcoin crash," Rob added.

"This thing is using an ARB_compute_shader," Sai commented. "That should give you an idea of how old it is."

"Joke's on them", Rob laughed. "These brand-new Macbook GPUs still aren't as powerful as the PC GPUs that were common 10 years ago."

"Sounds like the joke's on us," Marie observed. Meanwhile, Sai had finished extracting his files, had cleaned Chrome's local storage, had reloaded his IDE, and was now in the process of copying and pasting source code from the retrieved files into the browser.

"I never thought I'd say this, but I miss Xcode," Sai murmured.

"At least it beats Eclipse, eh?" Rob quipped. "What a piece of crap."

"At least Eclipse never did this," Sai said, pointing to the screen. When Marie and Rob leaned over to look, Sai hit Command-V a few times, and each time he did, an advertisement for diet pills was pasted into the window.

"Check the updates tab," Marie instructed. That tab usually contained the IDE vendor's blog, but today it sported the message, "DEFACED BY DARC FORCEZ" in large red text, and was crammed full of non-worksafe advertisements. After Sai closed it, copy and paste once again worked properly, and he was able to get back to work.

"Why do we even allow random Web pages to write to the clipboard?" Sai asked frustratedly.

"It's useful in a number of situations," Marie asserted. "Besides, native apps can do it. If Web apps couldn't, they wouldn't be as powerful as native apps."

"But shouldn't the Web browser ask for permission first?" Sai asked rhetorically.

"If Web browsers had to ask for permission to automate every little thing, regular users would be scared off," Marie stated. "Remember when Web pages had to ask for permission to use local storage? Most users were intimidated by the dialog and denied permission, and then complained when they couldn't save files locally."

"But the code executes in non-app contexts," Sai protested. "The Web is for more than just apps."

"Not this again," Marie goaned.

"People browse regular Web pages all the time," Sai continued, "and these days, pages all execute code with the full permissions of a native app, without the user having any control. You can't even browse with Javascript disabled, since most sites don't work without it."

"That's been the case ever since Javascript hit the scene," Rob pointed out. "Graceful degradation has always been a pipe dream. It's not worth the extra effort to support those few who browse without Javascript."

"All I'm saying," Sai said through gritted teeth, "is that maybe we shouldn't have tried to force all the functionality of native apps onto the Web. It may have made the Web a better app platform, but it also made the Web a decidedly worse browsing platform."

"We all know how much you love native development," Marie chided, "but single-platform development hasn't been economically viable in years, and I can't think of any other cross-platform runtime advanced enough or important enough that every platform vendor is forced to support it."

"Fine," Sai sighed, "maybe we need a new, more restricted runtime for browsing then."

"And where do you draw the line?" Marie pressed. "Hyperlinks and forms provide enough interactivity for basic mail readers, message boards, wikis, and many other traditional mainstays of the Web, but each of these applications benefit from the greater interactivity provided by Javascript."

"They also suffer for it," Sai maintained. "I'm not sure where you draw the line, but there has to be something better than what we have now."

"What were you working on anyway?" Rob interrupted, trying to steer the conversation back to a productive topic.

"I think we've got a memory leak on the server," Sai said, "but I can't track it down. I can see the leaked object in the heap dump, but I can't figure out why it's still being referenced."

"Try the memory profiler?" Rob suggested.

"It doesn't work anymore," Marie answered. "They redesigned V8's garbage collector a few months ago. It's a lot more efficient now, but it doesn't work with the memory profiler yet. And before you ask, we can't run the code in an older version of V8, since we're making extensive use of newer features."

"Besides, the memory profiler never worked reliably," Sai added. "At least, it never worked as well as the Leaks instrument. I really miss having that much detail. No, what I really miss is having decent tools to solve problems like this."

Rob could sense another argument brewing between Marie and Sai, and moved to cut it off before it could begin. Javascript was Marie's first programming language, she was very good at working around its weaknesses, and she still felt that rush that came with writing working code. Rob remembered feeling that way about Perl 30 years ago, and he suspected Sai once had similar feelings, but those feelings caused Marie to be defensive of her favorite language, which didn't mix well with Sai's cynicism.

"Bah, back when I was a Perl hacker, we didn't have any tools to detect reference cycles, and we were just fine," Rob bragged. "One time I even worked around a reference counting bug by storing an object as a string and deserializing it when I needed to use it."

"Oh, I've heard about that bug," Sai said with sarcastic enthusiasm. "They're going to fix it in Perl 6."

"Nice try, but Perl 6 was released years ago," Rob shot back.

"Really? I'm not sure anyone noticed," Sai huffed. "Fine, Perl 7 then."

"Don't even joke about that," Rob protested good-naturedly. "Anyway, how bad is this memory leak? We've got a new P0 that's causing the CEO to throw a fit."

"It can wait," Sai answered, logging into JIRA to check the new bug.

"This bug is hilarious," Marie said dryly after a couple of minutes. "Some jerk took out an ad with our ad provider that targets us specifically. It injects code into our site that hides all our advertisements and replaces our file selection screen with a rant how much our app sucks."

"So we just need to get the ad pulled?" Sai queried. "Why is that a job for engineering?"

"We can't get the ad pulled," Rob explained, rubbing his temples, "the ad exchange is fully automated, so there's nobody we can contact to get it pulled. Curated ad exchanges don't pay enough to keep the lights on. Hell, the even the uncurated exchanges barely cover our hosting costs."

"Even with our massive user base?" Marie sounded confused. "Then how do we make any money? I thought those ads paid our salaries."

"Same way all consumer software companies make money these days: VC funding," Rob replied. "We're hoping to attract enough users fast enough to get bought out by one of the tech giants. The CEO's more worried about the damage to our reputation than she is about the ad revenue."

"So this whole industry is funded by…" Marie began, a disgusted look on her face.

"Yes," Sai smirked, "It's funded by investors trying to siphon money from other investors. We're just the ball in a game played by these investors, and we're lucky enough to take home a salary as long as we remain in play."

"What choice do we have?" Rob chimed in. "The bottom fell out of native apps, users have never been willing to pay for Web apps, and ads just don't pay like they used to."

"There have to be other business models," Marie pleaded. "After my internship is over, I'm sure I can find a company that makes its money from its customers."

"Then I hope you enjoy writing crappy enterprise software," Rob said.

"Forget it," Marie said. "Let's just get this bug fixed."

"This never would have happened with a native app," Sai muttered.

"It's not that bad," Marie countered, trying to bring some cheer and perspective to the gloom that now hung over all three of them. "We just have to patch the XSS vulnerability."

"I should never have put all my savings in Bitcoin," Sai lamented, ignoring her.

"You and me both," Rob commiserated. "You and me both."



MAIKit: A Framework for Sharing Code Between iOS and OS X

In the year since Apple introduced Continuity to iOS 8 and OS X Yosemite, Mac app development has become more attractive to iOS developers. Although Apple's new Photos app ships with a private framework named UXKit, which implements a subset of UIKit on OS X, Apple has yet to provide developers a way to write cross-platform code. Third-party frameworks like Chameleon exist, but because UIKit lacks APIs for some features of OS X, apps built with UIKit feel limited on OS X. However, since UIKit and Appkit are very similar, many developers alias UIKit and Appkit classes and use methods common to both frameworks.

For the past few months, I've been experimenting with automating this technique by parsing the UIKit and AppKit headers to extract the interfaces common to both, and the result is MAIKit. I thought that even if this wasn't an ideal way to develop cross-platform code, if this is something a lot of developers are doing, we could all benefit by automating the process.

I'm releasing MAIKit now because it has already helped me write cross-platform code faster, but there is still much work left to do. The ruby script used to generate the framework is a showcase of bad code. It developed organically as I was figuring out how feasible this project was, and needs to be completely redone. I also plan to add category methods to fill in bits of functionality that are not similar across UIKit and AppKit (e.g. setting button titles).

If you think MAIKit is a good fit for your project, please check out the GitHub repo, where you can also find documentation and sample code. And if you feel like it, bug reports, feature requests, and pull requests are always welcome.


Avoiding Stringly-Typed Code in Storyboard View Controllers

At WWDC, Apple heavily promoted storyboards for both iOS and Mac development. Although storyboards aren't a good fit for every project, Apple is investing a lot of effort in improving storyboards, so it's becoming increasingly important for iOS and Mac developers to know how to use them. Segues are a fundamental building block of Storyboards, but the APIs provided to work with segues are some of the most stringly typed constructs in Cocoa and CocoaTouch. However, with a little reflection, we can create a better interface.

When using storyboards, it's not possible to connect IBOutlets between view controllers. When a view controller needs to store a reference to another view controller, it's common practice to override -prepareForSegue:sender:, and store the segue's -destinationViewController. Since this one method is called for every segue, and since the segues can only be differentiated by their identifiers, this leads to unwieldy if-else statements.

Since most view controllers only contain a few segues, I've always just gritted my teeth and followed this pattern when working with storyboards. But OS X storyboards will contain many more segues, and this pattern obviously does not scale well.

To solve this problem, we can override -prepareForSegue:sender: to call methods based on the segue's identifier. The following code does two things. First, if the view controller contains a property named segueTarget_<segue identifier>, it assigns the segue's -destinationViewController to that property. Second, if the view controller contains a method named -prepareForSegue:<segue identifier>sender:, it calls that method.

This behavior is inherited by both Objective-C and Swift subclasses of this view controller.

Of course, this is not a perfect solution. Like the stringly-typed version, this is brittle: It will break if you rename your segue without renaming the associated properties and methods. It would be much better if the segues themselves had targets and actions which could be set independent of their identifier.

I have created a Github repository with a view controller that implements this technique, as well as a similar technique for -shouldPerformSegueWithIdentifier:sender:. It's MIT-licensed, so please feel free to use it in your own code.


Carbon and Cocoa as a Metaphor for Objective-C and Swift

In the two most recent episodes of Core Intuition, Daniel Jalkut and Manton Reece talked about whether Swift will split the Apple third-party developer community into Swift and Objective-C sub-communities. Manton pointed out that the community was split in the early 2000s between Carbon and Cocoa, and although people tend to perceive OS X as a continuation of NeXTSTEP, it's actually, "a beautiful mix of classic Mac, NeXTSTEP, and new stuff". Should Mac and iOS development similarly become a beautiful mix of Objective-C and Swift, we can look forward to a smooth and eventual transition to Swift, but at the end of that transition, we may be left with large Objective-C apps that, like Carbon apps today, are increasingly out of place on Apple's platforms.

The Legacy of Carbon

It's amazing how well the Classic to OS X transition worked because of Carbon. Although some developers were unable to make the transition from Classic Mac OS to OS X, most of the important apps were able to transition without requiring disruptive and major rewrites. These apps may not have made it to OS X otherwise. However, the choice to migrate code to Carbon rather than to rewrite it in Cocoa may have created more work for those developers in the long run.

Carbon was created to allow Adobe and Microsoft to easily keep their flagship products on OS X. Office and the Creative Cloud apps are large C++ codebases, mostly because they share a lot of code with their Windows versions. As Apple has rolled out new technolgies that improve their platforms, these apps have struggled or been unable to adopt them. The Adobe apps were unable to transition to 64-bit until they moved their Mac UI code to Cocoa. InDesign only made the transition last year, at the same time that Adobe changed to a subscription pricing structure. Anyone who hasn't upgraded due to the pricing change is still running a Carbon version of InDesign.

But even the Cocoa version of InDesign is not a great platform citizen. OS X reports that it always uses significant energy, even when idle. Its dialog boxes are just an NSWindow with an NSView. Adobe draws text and buttons in the NSView, and tracks mouse clicks in the view manually. Since there are no NSTextFields or NSButtons, these dialogs are completely invisible to screen readers.

This is in stark contrast to Lightroom, which was written with Cocoa in mind from the beginning. It's still a cross-platform app, but all of its UI elements are subclasses of Cocoa classes. The app is accessible, and it doesn't use significant energy while idle. More importantly, it can quickly adopt new features of the OS.

This is possible because Adobe wrote Lightroom's interface entirely in Objective-C, and used Lua for the cross-platform logic. On Windows, Lightroom's GUI is written using native frameworks. Both of these GUIs provide the same API to Lua, so there is extra work required to make sure that API changes are implemented identically on both platforms. The key difference is that InDesign writes as much cross-platform code as possible, only writing native code where it has to, whereas Lightroom writes as much native code as possible, only writing cross-platform code for platform-agnostic logic.

It took Adobe seven years after Apple announced the demise of 64-bit Carbon for InDesign to complete the transition. And now, just a year after the InDesign team completed their Cocoa rewrite, Apple has announced another transition in Swift. Swift doesn't yet interoperate with C++, so Adobe can't simply rewrite the Objective-C parts in Swift.

InDesign is not the only example of a Carbon app struggling to keep up with the platform. Its main competitor, QuarkXPress, also made the transition to Cocoa last year. Also like InDesign, QuarkXPress has a C++ API that developers can use to write third-party plugins. QuarkXPress's API includes methods to add menu items to QuarkXPress's menus. With the transition to Cocoa, this API ceased to work on the Mac, and was replaced with a native Cocoa API. The C++ Menu API still exists and works on Windows, creating a situation where a plugin can be written entirely in C++, with the single compromise that menu-adding code must be rewritten on OS X.

Smooth transitions, like the transition from Carbon to Cocoa, incentivize development plans that lead to such compromises, because they require periodic incremental improvements. Each of these improvements costs less than a full rewrite, but the combined cost of all these improvements outweighs the cost of a rewrite. In addition, each improvement costs more than the last, as each change Apple makes taxes the app's original design further, and the previous incremental improvements each introduced further design constraints. At this point, it's a such large task to just update these programs to work on modern platforms at all that they don't work well and aren't good platform citizens.

How Objective-C Could Become the Next Carbon

There are many features of Swift that make it a great language for Apple to write its future frameworks in. It probably won't be long until some of these frameworks start to use Swift-only features like generics. Taking full advantage of platform improvements requires adopting new frameworks, and any Objective-C programs that wants to use these frameworks will need to write adapter code in Swift.

But Swift and Objective-C have different design philosophies. As time goes on, it will be harder to reconcile Objective-C's design philosophy with native Swift frameworks. In order to simply get their apps to work, developers will spend a lot of time figuring out how to refactor their existing Objective-C codebases to fit within the constraints imposed by Swift, leaving them with less time for other improvements.

The future could also bring a new Swift runtime, one that's incompatible with Objective-C. Apple took advantage of the 64-bit transition on the Mac to rewrite the Objective-C runtime. The 64-bit Mac runtime is the same as the iOS runtime. 32-bit Mac apps are left with the old runtime, and are thus unable to use some frameworks. A new Swift runtime could likewise cut off Objective-C apps from newer frameworks.

Any Objective-C app that gets as large and important as a Microsoft Office, Adobe Creative Suite, or Final Draft would be in the same position these apps are today. We'd still rely on them for day-to-day work, but they would become increasingly cumbersome to use, and increasingly expensive to maintain.

In contrast, a transition that breaks compatibility might be better for these apps in the long run. If Adobe had been forced to rewrite all their apps, it would have disrupted their development for a few years, but technology-wise, they would all be as well-off as Lightroom. Apple didn't have the leverage to make such a transition back then, but they do now. Ten years down the line, we may look back and wish that Apple had told developers that Objective-C was going away, and that they needed to completely rewrite their apps in Swift.


Please Don't File Duplicate Feature Requests for Swift

Chris Lattner, the creator of Swift, has recently asked developers to not intentionally file duplicate feature requests for Swift. The Swift team does not consider how many people have filed a request when prioritizing features, and processing duplicate requests take time away from development. However, he did encourage developers to always file bug reports, even if they think they might be duplicates, to ensure that no bugs are missed.

In my post arguing for message passing in Swift, I encouraged readers to file duplicate feature requests for the radars listed in that post. I have now updated that post to discourage duplicate feature requests. Please do not duplicate the radars in that post.

Third-party developers have often filed duplicate radars, at the encouragement of Apple engineers, as a way of voting on which bugs and feature requests are important to them. There have been public campaigns within the community to get developers to duplicate certain radars, like this campaign to support dynamic libraries and frameworks on iOS. (Apple announced framework support on iOS at WWDC 2014.)

Filing duplicates has always been inefficient. Those filing the radars have to take the time to describe the problem, and Apple employees need to read the radars and match them up to the correct duplicate. Many developers have asked Apple to open up the Radar system and allow them to vote directly on radars. The only response to this suggestion that I have ever heard from an Apple employee was that they understand that filing radars is not ideal for third-party developers, but that Apple needed Radar to remain somewhat opaque to non-employees.

After years of living with these limitations, filing radars, and talking with Apple employees, the third-party developer community has internalized the following two ideas about Radar.

  1. If it's not in a radar, Apple won't work on it. Filing radars is difficult and time-consuming, but it's the only option if you want Apple to change something.
  2. Radars with more duplicates get more attention. Duplicates are how Apple knows how many people a radar is important to.

Although not filing duplicates is a change for the community, it's important to honor Chris's request. As anyone who has worked in a large organization knows, what works for one team may not work for another. Swift is very different than most of the other products Apple produces, and may require different development policies. Filing duplicate radars isn't going to change how the team prioritizes features, and it reduces the time they have to work on the features you want in the language.

This does make filing radars even harder on third-party developers. Before, Radar abstracted away the fact that there are many diverse teams within Apple, and allowed developers to treat the company as if it was just one entity with uniform policies. I hope Apple will do something to clarify which teams want duplicate feature requests and which teams do not

In the meantime, please help to spread the word to the community that the Swift team doesn't want intentionally duplicate feature requests. Filing duplicate radars is a well-engrained bit of institutional knowledge, and it's going to require a lot of us blogging, tweeting and talking about it to change our behavior.