escaping the mac app store sandbox (or at least a small part of it)
DESCRIPTION
How to use File Presenters to create temporary files in a sandboxed mac app.TRANSCRIPT
Escaping)theMac$App$Sandbox
(or$at$least$a$small$part$of$it)
Ma#$Welch
Developer(working(on(iOS,(Mac,(and(node(apps(by(night,(and(on(the(Force.com(pla=orm(by(day.
ma#@welcher.net@_Ma#Welch_h#p://ma#wel.ch
Bearings
What%is%the%Mac%App%Sandbox?
App#Sandbox!is!an!access!control!technology!provided!in!OS!X,!enforced!at!the!kernel!level.!Its!strategy!is!twofold:
• App$Sandbox$enables$you$to$describe$how$your$app$interacts$with$the$system.$The$system$then$grants$your$app$the$access$it$needs$to$get$its$job$done,$and$no$more.
• App$Sandbox$allows$the$user$to$transparently$grant$your$app$addi=onal$access$by$way$of$Open$and$Save$dialogs,$drag$and$drop,$and$other$familiar$user$interac=ons.
(From&the&Apple&"App&Sandbox&Design&Guide")
Sandbox(Filesystem(Limita2ons
Specifically,+sandboxing+limits+an+app+to+only+those+files+or+directories+explicitly+opened+by+the+user.
For$most$cases$this$is$OK.$(In$fact,$Deckset$here$regularly$asks$permission$to$access$a$photo$I$want$to$use$that's$on$my$filesystem.)
Temporary)Files
The$problem$is$that$temporary$files$are$supposed$to$be$invisible$to$the$end$user.
What%kind%of%experience%would%this%be?
Now$mul(ply$this$bad$experience$by$10.$Or$100.$Or$however$many$hundreds$of$photos$(in$the$case$of$Bearings$app)$that$need$to$be$processed.
Workaround:*File*PresentersFile%Presenters?
Yes,%File%Presenters.
• Around(since(10.7,(File(Presenters((and(their("parents"(File(Coordinators)(have(made(working(with(the(file(system(in(a(mulDEthreaded,(mulDEprocessing(environment(easier(and(safer.
Yes,%File%Presenters.
• Around(since(10.7,(File(Presenters((and(their("parents"(File(Coordinators)(have(made(working(with(the(file(system(in(a(mulDEthreaded,(mulDEprocessing(environment(easier(and(safer.
Yes,%File%Presenters.
• Around(since(10.7,(File(Presenters((and(their("parents"(File(Coordinators)(have(made(working(with(the(file(system(in(a(mulDEthreaded,(mulDEprocessing(environment(easier(and(safer.
Yes,%File%Presenters.
• Around(since(10.7,(File(Presenters((and(their("parents"(File(Coordinators)(have(made(working(with(the(file(system(in(a(mulDEthreaded,(mulDEprocessing(environment(easier(and(safer.
Yes,%File%Presenters.
• Around(since(10.7,(File(Presenters((and(their("parents"(File(Coordinators)(have(made(working(with(the(file(system(in(a(mulDEthreaded,(mulDEprocessing(environment(easier(and(safer.
Implemen'ng)File)Presenters)for)Related)Items
Apple%has%repurposed%File%Presenters%to%enable%"Related%Items"%in%sandboxed%apps.
Related'Items
• Must&have&the&same&name&(minus&extension)&as&the&item&to&which&they&are&related
• Must&have&a&known9ahead9of9;me&extension
Implemen'ng)File)Presenters)for)Related)Items
Create&a&class&that&conforms&the&the&NSFilePresenter&protocol.&Implement&these&methods:
1. primaryPresentedItemURL#–#The#URL#for#the#original#file
2. primaryItemURL#–#the#URL#for#the#temporary#file#(the#same#as#above,#but#with#different#extension)
3. presentedItemOperationQueue#–#the#queue#on#which#the#app#will#perform#file#presentaGon#tasks
WRFilePresenter
// WRFilePresenter.m// Bearings//// Created by Matt Welch on 3/17/14.//
#import "WRFilePresenter.h"
@implementation WRFilePresenter{ NSOperationQueue* queue; NSURL* pFileURL; NSURL* tFileURL;
}
- (id) init { self = [super init]; if (self) { queue = [NSOperationQueue new]; [NSFileCoordinator addFilePresenter:self]; } return self;}
- (NSURL*) primaryPresentedItemURL { return pFileURL;}
- (NSURL *) presentedItemURL { return tFileURL;}
- (NSOperationQueue*) presentedItemOperationQueue { return queue;}
-(void) setURLs:(NSURL*)url { pFileURL=url; NSString *fURLS=[url absoluteString]; NSString *fURLSt=[NSString stringWithFormat:@"%@%@",fURLS,@"_temp_file_extension" ];
NSURL *surl = [NSURL URLWithString:fURLSt];
tFileURL=surl;}
@end
Implemen'ng)File)Presenters
Given&a&file&with&a&url&of&realFileURL,&implement&a&presenter&for&it:
WRFilePresenter *filePresenter=[[WRFilePresenter alloc] init];[filePresenter setURLs:realFileURL];
And$we're$all$set$(at$least$as$far$as$code$is$concerned).
XCode&Target&Setup
In#the#Project#Navigator,#under#the#"Info"#tab,#there#is#a#"Documents#Type"#sec=on.
• Set%"Extension"%to%be%the%known%temporary%file%extension.
• Add%NSIsRelatedItemType%of%type%Boolean%to%"Addi<onal%document%type%proper<es"%and%set%it%to%YES
XCode&Target&Setup
Demo
Further'Informa.on
More%in(depth%informa0on%can%be%found%at:h5p://ma5wel.ch/temporary(files(in(sandboxed(mac(apps/
This%presenta,on%is%on%github:h2ps://github.com/ma2welch/mac_sandbox_tempfiles_deckset
A"ribu'ons
• happiness)from)a)sandbox)1)h2ps://www.flickr.com/photos/celinesphotographer/326629023/
• App)Sandbox)Design)Guide)1)h2ps://developer.apple.com/library/mac/documentaIon/Security/Conceptual/AppSandboxDesignGuide/AboutAppSandbox/AboutAppSandbox.html
• Paperama)1)h2ps://www.flickr.com/photos/kasaa/3103799093/