Airstash with iOS - objective-c

Does anyone know of tutorials or existing projects for Airstash SDK iOS integration? I have the frameworks in my project, but the existing comments inside the header files aren't incredibly helpful for initial setup. I've been googling for one, but I get a deluge of tech announcement news instead of developer resources.
-Background-
The Airstash is going to be used with an already-developed iPad application that sets equipment preferences. The targeted equipment has already been developed and has no wireless connectivity, but does have USB capability. The proposed solution is to wirelessly upload files from the iPad to an Airstash connected to the equipment.

In the SDK release there are two directories: AirStashSDK and sdk-demo. The sdk-demo directory contains an XCode project that demonstrates usage of the SDK.
The AirStashSDK folder contains the AirStash.framework to include in your project, and a Resources folder that contains a couple xib files that you should include in your project and may customize. If you plan to customize these files you may want to copy them to a different directory so your changes are not lost if you update to a newer release of the SDK. The xib files are used to display progress while getting a file from the AirStash, or activity when saving a file to the AirStash.
To save a file to an AirStash, look at the saveFileAction: method in sdk-demo/AirStashSDK Demo/RootViewController.m.
- (void)saveFileAction:(NSString*)filename
{
NSLog(#"Save a file to AirStash. filename: %#", filename);
NSURL *docDir = [self getDocumentsDirectory];
NSString *filepath = [[docDir URLByAppendingPathComponent:filename] path];
airstash = [[AirStash alloc] init];
// Save is very simple.
[airstash saveFileToAirStash:filepath
presentingFrom:self
successBlock:^(void){
NSString *msg = [NSString stringWithFormat:#"Success saving file to AirStash: original filename: %#", filename];
NSLog(#"%#", msg);
[self presentAlertWithMessage:msg];
self.airstash = nil;
}
errorBlock:^(AirStashStatus errorCode, NSString *reason) {
NSString *msg = [NSString stringWithFormat:#"Problem saving file to AirStash: (%d) %#", errorCode, reason];
NSLog(#"%#", msg);
[self presentAlertWithMessage:msg];
self.airstash = nil;
}];
}
The demo app presents a list of the files in the app's documents directory. If the user taps on a file, it calls the saveFileAction: method to save the selected file to an AirStash. An app can allocate an AirStash object and make multiple calls to its methods, or as in this case, it just makes the one call and then releases it. (The demo app's presentAlertWithMessage: method just pops up a UIAlertView with the given message and an OK button so you can see that the action is complete.)
The demo app has a couple other buttons, one to get a file from an AirStash (and save it to the app's documents directory), and the other to get the URL of a file on an AirStash. The method used by the second button would be useful for apps that want to stream a file rather than download the whole thing at once.

Related

Xcode tvOS - Error: You don’t have permission to save the file “fileName.txt” in the folder “Documents”

I have an Xcode tvOS project in Objective-C.
I'm trying to save a sample fileName.txt file into the Documents folder with this code:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self applicationDocumentsDirectory];
NSString *path = [[self applicationDocumentsDirectory].relativePath
stringByAppendingPathComponent:#"fileName.txt"];
NSString* sampleText = #"Prova";
NSError* err;
[sampleText writeToFile:path atomically:YES
encoding:NSUTF8StringEncoding error:&err];
if(err != nil) {
NSLog(#"Error: %#", [err localizedDescription]);
}
}
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory {
NSLog(#"%#", [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject]);
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
}
#end
But I get this error when I run the app: Error: You don’t have permission to save the file “fileName.txt” in the folder “Documents”.
In iOS it works instead.
You don't have access to Documents on tvOS.
Here is from Apple (see for details here):
Local Storage for Your App Is Limited
The maximum size for a tvOS app bundle 4 GB. Moreover, your app can
only access 500 KB of persistent storage that is local to the device
(using the NSUserDefaults class). Outside of this limited local
storage, all other data must be purgeable by the operating system when
space is low. You have a few options for managing these resources:
Your app can store and retrieve user data in iCloud. Your app can
download the data it needs into its cache directory. Downloaded data
is not deleted while the app is running. However, when space is low
and your app is not running, this data may be deleted. Do not use the
entire cache space as this can cause unpredictable results.
Your app
can package read-only assets using on-demand resources. Then, at
runtime, your app requests the resources it needs, and the operating
system automatically downloads and manages those resources. Knowing
how and when to load new assets while keeping your users engaged is
critical to creating a successful app. For information on on-demand
resources, see On-Demand Resources Guide. This means that every app
developed for the new Apple TV must be able to store data in iCloud
and retrieve it in a way that provides a great customer experience.

How do I verify a file's existence in iCloud?

I know that the file exists, because I can download it, but I need to check to see whether it exists. I have tried using
[NSFileManager contentsOfDirectoryAtPath:error:]
but it gives me null. I don't understand why that is because I can still download the files that I'm looking for. Maybe it's an incorrect URL, but the URL that I'm using is the one that I printed at creation of my UIDocument that I'm looking for. Maybe I'm using the wrong method?
EDIT:
I have also tried using NSMetadataQuery, and I can get it to give back notifications, but it doesn't ever have results even though I can explicitly download the files I'm looking for.
To find files in iCloud, you use NSMetadataQuery. That will find both files that have already been downloaded as well as files that are in the user's account but which haven't been downloaded to the local device yet. Using NSFileManager will, at best, only find files that have already been downloaded.
You set it up with something like this:
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
[self setMetadataQuery:query];
[query setSearchScopes:#[NSMetadataQueryUbiquitousDataScope]];
[query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE '*'", NSMetadataItemFSNameKey]];
You'll want to observe NSMetadataQueryDidStartGatheringNotification, NSMetadataQueryDidUpdateNotification, and probably NSMetadataQueryDidFinishGatheringNotification. Then start the query:
[query startQuery];
With that done, you'll get notifications as the query discovers iCloud files. The notifications will include instances of NSMetadataItem, which you can use to get information like file size, download status, etc.
Use a metadata query - here is some sample code
/*! Creates and starts a metadata query for iCloud files
*/
- (void)createFileQuery {
[_query stopQuery];
if (_query) {
[_query startQuery];
}
else {
_query = [[NSMetadataQuery alloc] init];
[_query setSearchScopes:[NSArray arrayWithObjects:NSMetadataQueryUbiquitousDocumentsScope, NSMetadataQueryUbiquitousDataScope, nil]];
// NSString * str = [NSString stringWithFormat:#"*.%#",_fileExtension];
NSString *str = #"*";
[_query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, str]];
NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:#selector(fileListReceived) name:NSMetadataQueryDidFinishGatheringNotification object:_query];
[notificationCenter addObserver:self selector:#selector(fileListReceived) name:NSMetadataQueryDidUpdateNotification object:_query];
[_query startQuery];
}
}
/*! Gets called by the metadata query any time files change. We need to be able to flag files that
we have created so as to not think it has been deleted from iCloud.
*/
- (void)fileListReceived {
LOG(#"fileListReceived called.");
NSArray* results = [[_query results] sortedArrayUsingComparator:^(NSMetadataItem* firstObject, NSMetadataItem* secondObject) {
NSString* firstFileName = [firstObject valueForAttribute:NSMetadataItemFSNameKey];
NSString* secondFileName = [secondObject valueForAttribute:NSMetadataItemFSNameKey];
NSComparisonResult result = [firstFileName.pathExtension compare:secondFileName.pathExtension];
return result == NSOrderedSame ? [firstFileName compare:secondFileName] : result;
}];
//FLOG(#" results of query are %#", results);
for (NSMetadataItem* result in results) {
NSURL* fileURL = [result valueForAttribute:NSMetadataItemURLKey];
NSString* fileName = [result valueForAttribute:NSMetadataItemDisplayNameKey];
NSNumber* percentDownloaded = [result valueForAttribute:NSMetadataUbiquitousItemPercentDownloadedKey];
NSNumber *isDownloaded = nil;
NSNumber *isDownloading = nil;
NSError *er;
[fileURL getResourceValue: &isDownloaded forKey:NSURLUbiquitousItemIsDownloadedKey error:&er];
[fileURL getResourceValue: &isDownloading forKey:NSURLUbiquitousItemIsDownloadingKey error:&er];
FLOG(#" Found file %#", fileName);
}
}
From the docs:
In iOS, actively download files when required. Items in iCloud but not
yet local are not automatically downloaded by iOS; only their metadata
is automatically downloaded. The initial download of new iCloud-based
documents requires your attention and careful design in your app.
After you explicitly download such an item, the system takes care of
downloading changes arriving from iCloud.
Consider keeping track of
file download status as part of your iOS app’s model layer. Having
this information lets you provide a better user experience: you can
design your app to not surprise users with long delays when they want
to open a document that is not yet local. For each file (or file
package) URL provided by your app’s metadata query, get the value of
the NSURLUbiquitousItemIsDownloadedKeykey by calling the NSURL method
getResourceValue:forKey:error:. Reading a file that has not been
downloaded can take a long time, because the coordinated read
operation blocks until the file finishes downloading (or fails to
download).
For a file (or file package) that is not yet local, you can initiate
download either when, or before, the user requests it. If your app’s
user files are not large or great in number, one strategy to consider
is to actively download all the files indicated by your metadata
query. For each file (or file package) URL provided by the query, make
the corresponding item local by calling the NSFileManager method
startDownloadingUbiquitousItemAtURL:error:. If you pass this method a
URL for an item that is already local, the method performs no work and
returns YES.
Update: iOS7 should use NSURLUbiquitousItemDownloadingStatusKey instead of NSURLUbiquitousItemIsDownloadedKey.

Drag Files come across Sandbox(__CFPasteboardIssueSandboxExtensionForPath)

I processed drag operation from browser view to custom view.It work well in snow lepoard,but not in Mountain Lion with sandbox.
in browser view:
NSMutableArray* urls = [[[NSMutableArray alloc] init] autorelease];
..............put some NSUrl to urls array....................
[pasteboard writeObjects:[NSArray arrayWithArray:urls]];
in my receive custom view:
NSArray* pasteboardItems = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSString class]] options:nil];
NSArray* pasteboardItems2 = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:nil];
NSArray* pasteboardItems3 = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSImage class]] options:nil];
NSLog(#"%#",pasteboardItems);
NSLog(#"%#",pasteboardItems2);
NSLog(#"%#",pasteboardItems3);
my log is:
2012-08-09 18:33:43.886 iCollage[6885:303] __CFPasteboardIssueSandboxExtensionForPath: error for [/Users/xxxx/Library/Containers/xxxxxxxxxxxx/Data/Downloads/1343902069.jpg]
2012-08-09 18:33:44.546 iCollage[6885:303] ( "file://localhost/Users/xxx/Library/Containers/xxxxxxxx/Data/Downloads/1343902069.jpg")
2012-08-09 18:33:44.547 iCollage[6885:303] ( "file://localhost/Users/xxxxx/Library/Containers/xxxxxx/Data/Downloads/1343902069.jpg")
2012-08-09 18:33:44.547 iCollage[6885:303] ()
my question is:
1.how to fix this error __CFPasteboardIssueSandboxExtensionForPath;I refer the docs and found nothing about that.I am ensuer that i have the permission to access the file!google says, may be "startAccessingSecurityScopedResource" will help me, then i try and failed
2.why pasteboardItems2 have value?i write to pasteboard only url but not string.It disgusted me that I can get the url both from NSString type and NSUrl type! (I try drag a file from iFinder, the url will only exist in pasteboardItems but not pasteboardItems2).Anybody know why? I think the first problem will auto fixed when some one help me fix this problem.
I believe Apple answer question 1:
Important: Although you can support dragging file paths, in general,
you should avoid doing so unless you are certain that the destination
app will never be run in an app sandbox. If you use an NSString, OS X
has no way to know whether that string should be interpreted as a
path; thus, OS X does not expand the destination app’s sandbox to
allow access to the file or directory at that location. Instead, use
an NSURL, a bookmark, or a filename pasteboard type.
WRT to question 2, it looks like you have pasted URLs so reading NSURL objects would seem to be correct. However I think you should implement the dragging using the following code (also from the link above):
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
NSPasteboard *pboard = [sender draggingPasteboard];
if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {
NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
int numberOfFiles = [files count];
// Perform operation using the list of files
}
return YES;
}
You need to generate security-scoped URL bookmark data on the sender side, and turn that data back into a URL on the receiver side. There's some other stuff you have to do after that when you want to actually access the URL; the documentation elaborates.
The receiving application, when running in a sandbox, will not be able to handle bare paths. This is a core part of being sandboxed; you are not allowed to use bare paths or their corresponding URLs to access files that aren't in your sandbox container and haven't been explicitly handed to you by the user.
Your pasteboardItems read object of NSString type, but you dragged a file(with jpg extension), you should register for NSString type in your init method:
[self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeString]];
You need to have Document Types defined in your application so that the sandboxing mechanism knows your application should be opening files with those extensions. You can do this by clicking the project on the left in Xcode, and in the Info tab, under Document Types add a new document type for each extension.
You just need to fill in the name and extensions field.
Also if you want to persist your permission to access the files dragged onto your application, you can use this class to wrap up all that logic. https://github.com/leighmcculloch/AppSandboxFileAccess

Why does my data successfully write to a file on the iPhone simulator, but not my device?

I have the following code that saves the users sketch data to a file...
//Auto Save the sketch
NSString *filename = [NSString stringWithFormat:#"%#.png", sketchID];
CGImageRef imageRef = CGBitmapContextCreateImage(paintView.canvas.mBitmapContext);
UIImage* image = [[UIImage alloc] initWithCGImage:imageRef];
NSData* imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:filename atomically:YES];
CGImageRelease(imageRef);
[image release];
the SketchID is a unique alphanumeric value, so an example of the filename I'm saving to is "p1.png". The code I use to read the file is...
//Load the sketch where the user left off, if there is one
if(fileName != nil)
{
UIImage* image = [UIImage imageWithContentsOfFile:fileName];
if(image != nil)
{
.
.
This code seems to work fine when running on the simulator, but when I run it on the device, the fails to load the image. I new to iOS development and I'm still learning how files are stored. My questions are...
Why would saving/loading the file work on the simulator, but not the device?
When I create the filename that I want to save the data to, do I need to also include a path to a specific directory on the iPhone where the data should be properly stored? Or should "p1.png" work fine when I call the writeToFile: method? And what about the imageWithContentsOfFile: method?
Why would saving/loading the file work
on the simulator, but not the device?
There are tons of reasons, but the most common is trying to write to a location that isn't writable on the device, and the most common reason for that is writing to the application bundle itself.
All iPhone apps are "sandboxed" on the device, meaning they cannot access the filesystem outside of the directory the application is installed into.
When I create the filename that I want
to save the data to, do I need to also
include a path to a specific directory
on the iPhone where the data should be
properly stored? Or should "p1.png"
work fine when I call the writeToFile:
method? And what about the
imageWithContentsOfFile: method?
You can write to your application's temp directory (if you just need a temporarily place to put something during a run) or the Documents directory (if you need to store something more permanently, and also have it backed up by iTunes).
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
I found that the simulator is very forgiving in writing files but the devices of course are not. My problem was that I was creating filePath incorrectly that the simulator did not catch. Simulator should have caught this one.
Here was my wrong and correct swift code
docsDir!+"filename.dat" // wrong does not have path separator between dir and file
docsDir!.stringByAppendingPathComponent("filename.dat") // works since it puts the "/" in between the two.

Keep a reference to a file after it has moved in objective-c?

I have a Cocoa application that stores a reference to multimedia files (images, videos, etc) on the user's computer. I'm wondering if there is a way to get a reference to that file other that using a file path so that if the user moves that file to a different folder on their computer, I would still know where it is. I'm currently storing the array of file paths that are passed back from the standard Cocoa open dialogue:
-(void)addMultimediaDidEnd:(NSOpenPanel*)sheet
returnCode:(int)returnCode
contextInfo:(NSString *)contextInfo
{
if(returnCode == NSOKButton) {
[sheet orderOut:nil];
[self saveFiles:[sheet filenames]];
}
}
In OS X 10.6 (Snow Leopard), an NSURL can be converted to a file reference URL (using -[NSURL fileReferenceURL]) which references a file across moves while your application is running. If you want to persist this file reference, use +[NSURL writeBookmarkData:toURL:options:error:] passing the bookmark data generated with -[NSURL bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error]. The bookmark can be resolved later with +[NSURL URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error:] passing the bookmark data returned from +[NSURL bookmarkDataWithContentsOfURL:error:].
Prior to OS X 10.6, the same functionality (minus some network aware niceties) is available via the AliasManager, a Carbon-era interface to the OS X file alias system. There are a couple of Objective-C wrappers on top of the Alias Manager that make using it from Cocoa much nicer. My favorite is Wolf Rentzsch's additions to Chris Hanson's BDAlias (available on github).
Here's a quick example of using bookmarks to track files across moves:
- (NSData *)bookmarkFromURL:(NSURL *)url {
NSData *bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationMinimalBookmark
includingResourceValuesForKeys:NULL
relativeToURL:NULL
error:NULL];
return bookmark;
}
- (NSURL *)urlFromBookmark:(NSData *)bookmark {
NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark
options:NSURLBookmarkResolutionWithoutUI
relativeToURL:NULL
bookmarkDataIsStale:NO
error:NULL];
return url;
}
From https://github.com/ptrsghr/FileWatcher/blob/master/FileWatcherExample/Classes/FileWatcher.m