Get data contents of iCloud URL without UIDocument - ios

Is it possible to get the contents of an iCloud URL without having to go through UIDocument?

You can use the following code for downloding the contents of icloud url without creating the object of NSDocument. This will be worked for me...
-(void)loadData:(NSMetadataQuery *)query
{
if ([query resultCount] == 1)
{
NSMetadataItem *item = [query resultAtIndex:0];
NSURL *fromURL = [item valueForAttribute:NSMetadataItemURLKey];
NSURL *toURL=[NSURL fileURLWithPath:#"PATH_OF_FILE_TO_STORE_DATA"];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[coordinator coordinateReadingItemAtURL:fromURL options:0 writingItemAtURL:toURL options:NSFileCoordinatorWritingForReplacing error:nil byAccessor:^(NSURL *newReadingURL, NSURL *newWritingURL)
{
[[NSFileManager defaultManager] removeItemAtPath:newWritingURL.path error:NULL];
[[NSFileManager defaultManager] copyItemAtPath:newReadingURL.path toPath:newWritingURL.path error:nil];
}];
}
}

Related

NSBundleResourceRequest bundlePath

[UPDATE AT THE END OF THE QUESTION]
I use to play some audio files in my app.
I used to store the files in the app, but then I moved to On Demand Resourcesand NSBundleResourceRequest.
I have an IBAction to play a random file, and since I moved to ODR, it is not working. The only thing I changed was NSString *bundleRoot = [[NSBundle mainBundle] bundlePath]; (line 3)
NSBundleResourceRequest *request1;
- (IBAction)randomPlay {
NSString *bundleRoot = [[request1 bundle] bundlePath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:bundleRoot error:nil];
NSPredicate *fltr = [NSPredicate predicateWithFormat:#"self ENDSWITH '.mp3'"];
NSArray *onlyMP3s = [dirContents filteredArrayUsingPredicate:fltr];
.
.
.
audio = [onlyMP3s[arc4random_uniform((uint32_t)onlyMP3s.count)]
stringByReplacingOccurrencesOfString:#".mp3" withString:#""];
[self playSelectedAudio:audio];
}
The dirContents looks like:
when it is supposed to contain the mp3 files.
What am I doing wrong with bundleRoot?
My viewDidload is
- (void)viewDidLoad {
[super viewDidLoad];
tagsSet = [NSSet setWithObjects:#"sounds", nil];
request1 = [[NSBundleResourceRequest alloc] initWithTags:tagsSet];
[request1 conditionallyBeginAccessingResourcesWithCompletionHandler:^
(BOOL resourcesAvailable) {
if (resourcesAvailable) {
//don't do nothing. just use the file slater
} else {
[request1 beginAccessingResourcesWithCompletionHandler:^
(NSError * _Nullable error) {
if (error == nil) {
//download the files
} else {
}
}];
}
}];
I actually have another method to play a selected audio (not a random one)
-(void)playSelectedAudio:(id)audioToPlay {
NSURL *url = [NSURL fileURLWithPath:[[request1 bundle]
pathForResource:audioToPlay ofType:#"mp3"]];
and it is working fine.
So there is a problem in the bundleRoot, is is not where my files are located.
[UPDATE]
I actually did
- (IBAction)randomPlay {
NSURL *bundleRoot = [NSURL fileURLWithPath:[[request1 bundle]
pathForResource:#"" ofType:#"mp3"]];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtURL:bundleRoot
includingPropertiesForKeys:[NSArray array]
options:0 error:nil];
NSPredicate *fltr = [NSPredicate predicateWithFormat:
#"self ENDSWITH '.mp3'"];
NSArray *onlyMP3s = [dirContents
filteredArrayUsingPredicate:fltr];
.
.
.
audio = [onlyMP3s[arc4random_uniform((uint32_t)onlyMP3s.count)]
stringByReplacingOccurrencesOfString:#".mp3"
withString:#""];
[self playSelectedAudio:audio];
}];
}
and it is ALMOST working but is is always playing the first audio in the directory.
Didn't you forget beginAccessingResourcesWithCompletionHandler: call to actually download resources?
- (IBAction)randomPlay {
NSBundleResourceRequest *request1; // I hope you properly initialize this request :)
[request1 beginAccessingResourcesWithCompletionHandler:^(NSError * _Nullable error) {
if (error != nil) {
// Probably, add here dispatch_async(dispatch_get_main_queue(), ^{
NSString *bundleRoot = [[request1 bundle] bundlePath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:bundleRoot error:nil];
NSPredicate *fltr = [NSPredicate predicateWithFormat:#"self ENDSWITH '.mp3'"];
NSArray *onlyMP3s = [dirContents filteredArrayUsingPredicate:fltr];
.
.
.
[self playSelectedAudio:audio];
// You will probably need to invoke [request1 endAccessingResources] later, when you finish accessing your audio.
}
else {
// handle error
[request1 endAccessingResources];
}
}
}
Several important notes from NSBundle.h:
The completion block will be invoked on a non-main serial queue when the resources are available or an error has occurred.
So you will probably need to use additional dispatch_async to main queue in completion handler. But this depends solely on your code.
Be sure to always invoke the -endAccessingResources method to balance a call to the begin method, even in the case of an error in the completion handler.

Reading NSData in parts

I am implementing extensions in ios8. In this import function returns a url. I need to read data from it and save in my app.
Here is the code.
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
NSError *error;
__block NSData *data;
[fileCoordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
data = [NSData dataWithContentsOfURL:newURL];
//saving in document directory
}];
My question is, if file is too big, dataWithContentsOfURL results in crash due to out of memory.
I wanted a method to read data from that url in parts, save in my documents, then read next part and keep appending. Thus it won't have memory issue.
Can someone help.
Found two solutions.
1- Used NSURLConnection. Using NSFileHandle wrote data in file.
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data1{
// [data appendData:data1];
[self.currentFileHandle seekToEndOfFile];
[self.currentFileHandle writeData:data1];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[self.currentFileHandle closeFile];
self.currentFileHandle = nil;
}
Instead of converting it in data and then saving. Used following to copy file.
NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fileCoordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingWithoutChanges error:nil byAccessor:^(NSURL *newURL) {
NSFileManager * fileManager = [[NSFileManager alloc] init];
NSError * error;
NSArray *paths = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *urlDestination = [paths lastObject];
urlDestination = [urlDestination URLByAppendingPathComponent:[url lastPathComponent]];
if([[NSFileManager defaultManager] fileExistsAtPath:[urlDestination relativePath]]){
[[NSFileManager defaultManager] removeItemAtPath:[urlDestination relativePath] error:nil];
}
BOOL success = [fileManager copyItemAtURL:url toURL:urlDestination error:&error];
if (success) {
}
}];
I used 2nd solution. Sorry for wrong alignment. I tried a lot, but not
coming properly.

fail to load iWork file url in documentPicker:didPickDocumentAtURL:

I'm working on an iOS app which can import cloud files using DocumnetPicker, Under Xcode6.1, iphone5s(IOS8.1.1). It's strange that url of .number/.key file could not be load successfully, but others like .pdf/.jpg are fine.
My UTIS:
+ (NSArray*) supportedDocumentTypes
{
return #[(__bridge NSString*) kUTTypeContent,
#"com.apple.iwork.pages.pages",
#"com.apple.iwork.numbers.numbers",
#"com.apple.iwork.keynote.key"];
}
documentPicker:didPickDocumentAtURL: is defined the same in newbox sample:
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
BOOL startAccessingWorked = [url startAccessingSecurityScopedResource];
NSURL *ubiquityURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSLog(#"ubiquityURL %#",ubiquityURL);
NSLog(#"start %d",startAccessingWorked);
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
NSError *error;
[fileCoordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
NSData *data = [NSData dataWithContentsOfURL:newURL];
NSLog(#"error %#",error);
NSLog(#"data %#",data);
}];
[url stopAccessingSecurityScopedResource];
}
I believe those are directories (containers) technically, which is why .pdf works but not the .numbers, etc.

iCloud Folder Creating and Saving in iOS

I need to create Folder in Cloud and save my .txt Text File into that Folder.
So i created folder with following codes in AppDelegate
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSError *error = nil;
if(ubiq)
{
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *rootURL = [fm URLForUbiquityContainerIdentifier:nil];
NSURL *newFolder = [NSURL URLWithString:#"Notes" relativeToURL:rootURL];
[fm createDirectoryAtURL:newFolder withIntermediateDirectories:YES attributes:nil error:&error];
if(error)
{
NSLog(#"Error");
}
else
{
NSLog(#"NO Error");
}
}
After i test it , It show No Error.
So i assumed that it created Notes Folder in iCloud.
However when i save my text document into that folder with following codes , it showing error and not save.
NSString *file = [NSString stringWithFormat:#"%#.txt",fileName];
NSURL *ubiq = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
NSURL *ubiquitousPackage =
[[[ubiq URLByAppendingPathComponent:#"Documents"] URLByAppendingPathComponent:#"Notes"]
URLByAppendingPathComponent:file];
Note *doc = [[Note alloc] initWithFileURL:ubiquitousPackage];
[doc saveToURL:[doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (!success)
{
NSLog(#"Error");
}
else
{
doc.noteContent = self.txtView.text;
[doc updateChangeCount:UIDocumentChangeDone];
}
}];
And the error message is
Foundation called mkdir("/private/var/mobile/Library/Mobile Documents/35QNLTQU29~com~myapp~iCloudTest/Documents/Notes/(A Document Being Saved By iCloudTest)"), it didn't return 0, and errno was set to 2.
How can i solve it?

NSFileManager not saving Data

I am trying to save data into a directory created by NSFileManager but when I try and retrieve the data and NSLog it I get null. Also, when you create a directory does that mean you create a folder at a specified url path? Heres the code I am using
NSError *error = nil;
NSFileManager *manager = [[NSFileManager alloc] init];
NSArray *urlsArray = [manager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSLog(#"%#", urlsArray);
// This will create a new url and append a photo title to the end
NSURL *url = [[urlsArray lastObject] URLByAppendingPathComponent:[NSString stringWithFormat:#"%#", [self.recentPhoto objectForKey:FLICKR_PHOTO_TITLE]]];
NSLog(#"%#", url);
NSString *urlString = [NSString stringWithFormat:#"%#", [url absoluteString]];
NSLog(#"%#", urlString);
//create the directory
if(![manager fileExistsAtPath:urlString isDirectory:YES]){
BOOL success = [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:&error];
if (!success) {
NSLog(#"Error creating data path: %#", [error localizedDescription]);
}
}
//get url for a photo image and then store it.
NSURL *imageURL = [FlickrFetcher urlForPhoto:self.recentPhoto format:FlickrPhotoFormatLarge];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:imageURL];
[imageData writeToURL:url atomically:YES];
//get data to check if its stored
NSData *checkImageData = [[NSData alloc] initWithContentsOfURL:url];
//This returns null
NSLog(#"%#", checkImageData);
//this returns 0
NSLog(#"%d", [manager isReadableFileAtPath:urlString]);
There are several issues with your code.
This:
NSURL *url = [[urlsArray lastObject] URLByAppendingPathComponent:[NSString stringWithFormat:#"%#", [self.recentPhoto objectForKey:FLICKR_PHOTO_TITLE]]];
should be:
NSURL *url = [[urlsArray lastObject] URLByAppendingPathComponent:[self.recentPhoto objectForKey:FLICKR_PHOTO_TITLE]];
This:
NSString *urlString = [NSString stringWithFormat:#"%#", [url absoluteString]];
should be:
NSString *urlString = [url path];
This:
BOOL success = [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:&error];
should be:
BOOL success = [manager createDirectoryAtURL:[url URLByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:&error];
This:
[imageData writeToURL:url atomically:YES];
should be:
NSError *error = nil;
BOOL res = [imageData writeToURL:url options:NSDataWritingAtomic error:&error];
if (!res) {
NSLog(#"Unable to write to %#: %#", url, error);
}
The primary issue was the use of absoluteString instead of path to convert the URL to a file path. Secondary was passing the filename instead of the path when creating the directory. The needless use of stringWithFormat: didn't cause any issues but please break that habit now. Only use stringWithFormat: when you actually need to format a string. It is NOT needed to assign a string to a string variable.
You didn't specify a file name for your image inside the directory, this can be solved by adding this line right after the directory is created :
// Right after the directory is created (I suppose the format is png) :
url = [url URLByAppendingPathComponent:#"image.png"];
// Now you can continue with the rest of your code :
//get url for a photo image and then store it.
NSURL *imageURL = [FlickrFetcher urlForPhoto:self.recentPhoto format:FlickrPhotoFormatLarge];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:imageURL];
[imageData writeToURL:url atomically:YES];
//get data to check if its stored
NSData *checkImageData = [[NSData alloc] initWithContentsOfURL:url];
//This returns null
NSLog(#"%#", checkImageData);
//this returns 0
NSLog(#"%d", [manager isReadableFileAtPath:urlString]);
I have never created an instance of a file manager. I have always simply used the default.
See if this helps:
NSFileManager *manager = [NSFileManager defaultManager];

Resources