When iTunes Music Store first came out I studied it and decided to buy music off it. Not because I trusted Apple, but because I had a spare Mac that I could use as a DRM lifeboat.
I copied my purchases as I made them to the old Mac. The Mac was also disconnected from the Internet after its initial authorization. Finally, I had software in place to extract the audio I purchased even if Apple pulled the DRM football from me just as I tried to kick it.
Fortunately Apple’s iTMS DRM dog never barked. But after today’s Amazon Ebook controversy I decided it’s time to share my similar Amazon Ebook Insurance Policy:
Have the Kindle’s network turned off most of the time. I do this to conserve battery on my Kindle 3, but it has the nice side-effect that Amazon can’t remotely delete content at its whim.
Buy Chronosync and set it up to copy purchased Ebooks to my Mac whenever I connect+charge my Kindle. That way I can recover even from an unexpected remote wipe from Amazon.
Use (and hopefully donate to) Calibre and then install plugins to strip Amazon’s DRM from your purchased Ebook files. Calibre can then export your .azw and .azw1 files to unencumbered .mobi and .epub formats.
Update: Zachary West offers specific steps on converting Amazon to ePub using Calibre.
One of the features Apple took away in Xcode 4 was splits.
predicted headline feature of Xcode 5: splits
But it turns out you can clumsily emulate them with Xcode 4’s Assistant Editors:
@rentzsch like text splits? already there. View > Assistant Editor > Add Assistant Editor (and various shortcuts)
@kongtomorrow that only works when you have an assistant editor showing. Very rarely that I do. Need it for standard editor
@rentzsch @kongtomorrow You can open the same file in the assistant editor and then stack the editors vertically in your window.
@tgaul @rentzsch ooh, thanks, thought that was there somewhere. View > Assistant Editor > All Editors Stacked Vertically
@kongtomorrow @tgaul ah that’s clumsy, but it will work when I’m desperate. Thanks!
Technical Note TNNaN: OpenSSL on OS X
Long story short: we screwed up when we included OpenSSL (libcrypto) in OS X in the first place.
(We learned our lesson and didn’t repeat the mistake with iOS.)
Now there’s some transitionin’ to do.
Recommendations
New Code Should Use SecTransform. SecTransform is cool: it’s high-level, rather declarative, fast and even leverages GCD. It solves OpenSSL’s issues described below in the Backgrounder section.
Code That Uses OpenSSL Just for Hashing Should Switch to CommonCrypto. CommonCrypto provides a thin OpenSSL compatibility shim for common cryptographic message digests. It’s called COMMON_DIGEST_FOR_OPENSSL and lives in CommonDigest.h.
Apps That Need OpenSSL Should Include Their Own Copy. You should stop using the system’s supplied libcrypto.dylib, build your own and link it into your app.
We know that’s a pain, but this project should help you out.
Add -isystem "$(SRCROOT)/openssl-1.0.1c/include" to OTHER_C_FLAGS to pick up your project’s local OpenSSL headers instead of the system’s headers (which are outdated and throb with deprecation attributes).
Backgrounder
The crux of the problem is OpenSSL doesn’t offer API compatibility between versions.
We’d love to ship updated versions of OpenSSL, but there’s only two feasible routes, both of which are seriously problematic:
Apps link to /usr/lib/libcrypto.dylib symlink. In theory we should be able to always point that to the latest version of libcrypto.dylib and apps get free security + capability updates.
In practice we’d break shipping apps since the APIs are different. So it’s kinda stuck. (Notice we’re still shipping v0.9.8r when v1.0.1c is the latest.)
Apps link to a specific version of libcrypto. For example: /usr/lib/libcrypto.0.9.8.dylib.
Now your app doesn’t get security + capability updates for free. When there’s a security hole, it’s now our job to backport the fixes in the latest OpenSSL into the old-ass version your app is linked against.
Nobody wins in this case.
Ideally the OpenSSL project would do a better job at API compatibility, but it’s really not in their unixy source-code-oriented worldview. Sure we could get big into the project to try to improve it, but we’d rather put the resources into making OS X rock harder.
Both SecTransform and lowly CommonCrypto offer API compatibility, allowing us to add functionality and fix security problems even in shipping apps, which is awesome for users.
Apple has started deprecating OpenSSL on Mac OS X 10.7 SDK and later. I guess they’d prefer you use SecTransform or something. Unfortunately SecTransform isn’t on iOS yet and even it were, some of us have codebases that aren’t Apple-platform exclusive. Feels kind of like a dick move to me.
Anyway, I continued to use OpenSSL in my code — I just stuck a big fat
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
on the top of my OpenSSL-using source files.
Unfortunately that turns off deprecation warnings for the entire file. I tried downloading OpenSSL (/usr/include/openssl/opensslv.h’s OPENSSL_VERSION_TEXT indicates you want v0.9.8r) and using their header files directly but was stumped:
lazytwitter: how do I get Xcode to prefer my local openssl header dir to the SDK’s (that’s littered with with deprecation attributes)?
(setting
ALWAYS_SEARCH_USER_PATHS,HEADER_SEARCH_PATHS&USER_HEADER_SEARCH_PATHSdoesn’t seem to be enough or I’m doing it wrong)
Fortunately Christopher Lloyd answered my query:
@rentzsch -isystem <myincludedir> added using other c flags should do it
I just tried it and it works. Thanks again, Christopher!
Update: I take back my “feels kind of like a dick move” statement. There are good reasons for Apple to deprecate OpenSSL, I just wish they wrote a Technote about it. So I wrote one for them as penance. Apologies, Apple peeps.
Lich is my new mostly-human-readable binary data format. Check it out if you like bencode or need a space-efficient JSON.
Second vintage tactic I wish Apple used with Maps: align Apple resources (a la WebKit) with a promising open source effort (OpenStreetMaps).
I agree with Buzz here: feels like a missed opportunity for Apple and indeed humanity.
Apple uses OpenStreetMaps in iPhoto for iOS, so it’s not like they never heard of the project. I can only surmise somehow OpenStreetMaps wasn’t a fit (or Apple knew it was about to pour a billion dollars into a project and didn’t want to share the project’s fruits with competitors).
See also: llvm and Facebook’s Open Compute Project.
I started a new Apple-developer-related podcast with my friend Andrew Pontious called Edge Cases.
To kick off the show we’re going to catch up with our backlog and post an episode each day this week.
Click here to subscribe to the show.
You can also follow the show on twitter: @edgecasesshow.
Special thanks to Victoria Wang for the website design and coding.
Since the launch of the Mac App Store, a common question potential customers ask developers is “Should I buy your app directly or through the Mac App Store?”
Developers have been remarkably cagey, mostly replying with the non-answer “choose whichever is better for you”.
Fortunately Apple now only accepts sandboxed Mac apps, clarifying the situation: customers should buy Mac apps directly unless there’s a good reason not to.
Here are some reasons why it’s preferable to buy non-sandboxed apps directly from developers:
Better App User Experience: Non-sandboxed apps can auto-navigate the user to correct folders in Open/Save panels, run user-written AppleScripts, control iTunes* and install PDF Services automatically.
More features: Non-sandboxed BBEdit can directly edit files that require administrator privileges, non-sandboxed OmniFocus can automatically determine the selected document in the Finder when creating a clipping. There’s lots more.
Better Data Integrity: Document-based Core Data apps are incompatible with Sandboxing. One work-around is to disable disk-based journaling, increasing the risk of data corruption. It appears sandboxed Core Data Mac apps will need to switch to packages for a long-term solution, changing file formats for no good reason (and packages are less convenient for data sharing than flat files).
More and Faster Updates: Different developers follow different update schedules, but typically direct apps get updated more often and with less latency than Mac App Store apps. Some developers intentionally throttle down their direct update schedule to match Apple’s delays and avoid customer confusion, but that’s the developer’s decision.
Less Risk of Losing Your Software Investments: I bought Divvy via the Mac App Store. Unfortunately Divvy relies on Apple’s Accessibility APIs, which aren’t allowed for sandboxed apps. That means aside from minor bug fixes, I will no longer receive updates for the application I purchased.
Some developers are going out of their way to allow seamless cross-grading from Mac App Store versions of their apps to direct apps, which is commendable and helps alleviate somewhat the situation Apple has created.
Sandboxing is just the latest App Store rule change, I’m sure there’s more to come. All things being equal, it’s safer to buy directly instead of being cut off from your own software based on an arbitrary Apple policy change.
More Money Goes to the Developer: For a $10 application, only $7 goes to the developer when you buy it through the Mac App Store. For a direct purchase, it’s more on the order of $9.
The fact more developers weren’t encouraging customers to buy directly is a testament to how much Mac developers care about User Experience — they’re willing to forego the extra profit of selling direct to make things easier for their customers.
However, as alluded to above, there are downsides to buying directly. Buying through the Mac App Store offers these benefits:
Better Purchasing Experience: Apple probably already has the customer’s credit card on file. No .zip to unpackage and drag into the right place. It Just Works.
Better Maintenance Experience: Buy a new Mac, launch App Store.app and enter your Apple credentials. Ta-da, a list of the apps you purchased ready to reinstall. No trying to remember what apps you previously had installed, who wrote them, what the developer’s website is or what your license key was.
iCloud Access: Apple has decided only Mac App Store apps (and thus now sandboxed apps) can access iCloud. Fortunately this isn’t much of an issue since the smart folks at SmileOnMyMac have already demonstrated a work-around of having a “bridge” Mac App Store application which allows its directly-sold PDFPen to access iCloud as well.
Vetted By Apple: I don’t hold this benefit in very high regard, but it does add a safety net versus downloading and running arbitrary software off the web. For example, I’m looking forward to 10.8’s Gatekeeper as a way of limiting the software my mother downloads and attempts to run on her Mac (though this is apparently possible since 10.6).
The bottom line is that sandboxing has effectively collapsed the ambiguity and customers should now purchase their apps directly instead of through the Mac App Store.
* Update: Originally I wrote here “send Growl notifications”. I was corrected by Growl Project CEO Chris Forsythe that the version of Growl in the Mac App Store appears to now listen on a machine-local network port, which works around sandbox-disallowed Mach messaging.
My thinking was fuzzy in regards to what’s wrong with git submodules. So I finally opened up an editor window and started typing how I think an Ideal Submodule System should work. I wound up with this:
The subproject is copied into the super-project’s repo. At least a snapshot of it, if not the entire history. It’s a fact of life external resources have a habit of disappearing — this helps makes your project resilient.
git clone foo is enough. None of this git clone --recursive foo or git submodule init && git submodule update business.
Can easily pull subproject updates.
Can easily push subproject updates.
Can easily handle super-project branching.
Super-project’s commits don’t wind up in subproject’s history.
Subproject’s commits don’t wind up in super-project’s history.
Armed with clearer thinking, I reexamined my options and have settled on Avery Pennarun’s git-subtree.
Unfortunately its CLI UX is lacking (you have to specify the subproject’s entire remote repo URL each time you pull or push) and Avery hasn’t accepted pull requests or budged the project for a year. Fortunately Helmo forked Avery’s repo, added .gittrees support and push-all and pull-all subcommands, and generally seems to be keeping on top of pull requests and moving the project forward.
I just started using git-subtree, but so far it’s making my life better than before.
P.S. Here’s a short primer on using git-subtree.
Update: Hmm, I discovered and turned to Helmo’s fork after I got tired of reentering my remote repo URL each time I wanted to push or pull a subtree. Turns out Helmo’s fork is currently unstable — the tests don’t pass and there’s a definite bug in it when adding a repo as a subtree that doesn’t show up until you attempt push it back. So I recommend using Avery’s original repo until it’s fixed.
Update 2: I fixed the bug and tests are passing in my git-subtree fork. git subtree push-all away!
Sometimes Chrome has trouble restoring its windows and tabs upon launch (the windows and tabs open, but their content never loads). The worst thing about this bug is that it tends to happen only when I have large number of windows and tabs to restore.
I’ve added the following JSTalk script to my nightly backup routine. It writes out the names and URLs of all open tabs to a text file:
var chrome = [SBApplication application:'Google Chrome'];
var windows = SBElementArrayToJSArray([chrome windows]);
var output = '';
windows.forEach(function(window){
var windowName = [window name];
if (windowName != 'New Tab') {
var tabs = SBElementArrayToJSArray([window tabs]);
output += windowName + '\n';
tabs.forEach(function(tab){
output += '\t' + [tab title] + '\n';
output += '\t' + [tab URL] + '\n\n';
});
}
});
Date.prototype.getShortWeekdayName = function(){
return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][this.getDay()];
};
var tabsBackupFileName = 'tabs_' + (new Date()).getShortWeekdayName() + '.txt';
var tabsBackupFilePath = [@"~/Documents/backup/browser" stringByExpandingTildeInPath];
tabsBackupFilePath = [tabsBackupFilePath stringByAppendingPathComponent: tabsBackupFileName];
[[NSString stringWithString:output] writeToFile:tabsBackupFilePath atomically:NO encoding:NSUTF8StringEncoding error:nil];
function SBElementArrayToJSArray(sbArray) {
var sbIdx = 0;
var sbCount = [sbArray count];
var result = [];
for (; sbIdx < sbCount; sbIdx++) {
result.push([sbArray objectAtIndex:sbIdx]);
}
return result;
}
It uses Cocoa’s Scripting Bridge to enable JSTalk to talk to Chrome via its AppleScript interface.