Home > Articles > Mobile Application Development & Programming

  • Print
  • + Share This
This chapter is from the book

Handy Routines

This section presents a number of extremely simple routines that work together to allow you to manage files across your sandbox and cloud Documents folders. These methods assume your files live at the top level of these folders for several reasons:

  • Both the container and sandbox Documents folders are made accessible to your users via Settings and iTunes. Therefore, they are directly visible.
  • Neither system supports subfolders at this time.
  • Your users own these folders directly, according to Apple’s guidelines, and should have full access and visibility to all materials contained within.

Local URLs

Working with the sandbox Documents folder involves little more than querying for the documents directory. These methods return the documents folder and URLs that point inside that folder. This localFileURL: method does not test the URL it returns to see if it points to a valid object.

+ (NSString *) localDocumentsPath

{
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
        NSUserDomainMask, YES) objectAtIndex:0];
}
+ (NSURL *) localDocumentsURL
{
    return [NSURL fileURLWithPath:[self localDocumentsPath]];
}
+ (NSURL *) localFileURL: (NSString *) filename
{
    if (!filename) return nil;
    NSURL *fileURL = [[self localDocumentsURL]
        URLByAppendingPathComponent:filename];
     return fileURL;
}

Locating the Ubiquitous Documents Folder

Supply a container identifier and NSFileManager returns URLs pointing to the root of that cloud folder. Appending Documents produces the ubiquitous documents URL.

+ (NSURL *) ubiquityDataURLForContainer: (NSString *) container

{
    return [[NSFileManager defaultManager]
         URLForUbiquityContainerIdentifier:container];
}
+ (NSURL *) ubiquityDocumentsURLForContainer: (NSString *) container
{
    return [[self ubiquityDataURLForContainer:container]
        URLByAppendingPathComponent:@"Documents"];
}

When you have that folder’s URL, you can append file names to it to point to items within the Documents subfolder. Like the localFileURL: method, the following methods do not test the URL it returns to see if it points to a valid object:

+ (NSURL *) ubiquityDataFileURL: (NSString *) filename

   forContainer: (NSString *) container
{
   if (!filename) return nil;
   NSURL *fileURL =
       [[self ubiquityDataURLForContainer:container]
           URLByAppendingPathComponent:filename];
   return fileURL;
}
+ (NSURL *) ubiquityDocumentsFileURL: (NSString *) filename
    forContainer: (NSString *) container
{
   if (!filename) return nil;
   NSURL *fileURL =
       [[self ubiquityDocumentsURLForContainer:container]
           URLByAppendingPathComponent:filename];
return fileURL;
}

Retrieving Files

The first three of these methods test whether a file exists, locally or in the data or documents ubiquity. The final method uses these tests to retrieve a file URL, searching first the local folder and then the cloud. If a matching file is found, it returns that URL.

+ (BOOL) isLocal: (NSString *) filename

{
    if (!filename) return NO;
    NSURL *targetURL = [self localFileURL:filename];
    if (!targetURL) return NO;
    return [[NSFileManager defaultManager]
        fileExistsAtPath:targetURL.path];
}
+ (BOOL) isUbiquitousData: (NSString *) filename
    forContainer: (NSString *) container
{
    if (!filename) return NO;
    NSURL *targetURL = [self ubiquityDataFileURL:filename
        forContainer:container];
    if (!targetURL) return NO;
    return [[NSFileManager defaultManager]
        fileExistsAtPath:targetURL.path];
}
+ (BOOL) isUbiquitousDocument: (NSString *) filename
    forContainer: (NSString *) container
{
    if (!filename) return NO;
    NSURL *targetURL = [self ubiquityDocumentsFileURL:filename
        forContainer:container];
    if (!targetURL) return NO;
    return [[NSFileManager defaultManager]
       fileExistsAtPath:targetURL.path];
}
+ (NSURL *) fileURL: (NSString *) filename
    forContainer:(NSString *)container
{
    if ([self isLocal:filename])
         return [self localFileURL:filename];
    if ([self isUbiquitousDocument:filename
         forContainer:container])
        return [self ubiquityDocumentsFileURL:filename
           forContainer:container];
    if ([self isUbiquitousData:filename
       forContainer:container])
        return [self ubiquityDataFileURL:filename
           forContainer:container];
    return nil;
}

Setting Ubiquity

The following method applies ubiquity to a file based on its name, not its current location. This routine checks for each possible file condition. If the file is already found where it’s supposed to end up, the method returns with success. If the file cannot be found, it fails. Otherwise, the code moves the file to or from the cloud as requested, wrapping away the otherwise fussy implementation details of the native NSFileManager method.

+ (BOOL) setUbiquitous:(BOOL)yorn for:(NSString *)filename
    forContainer:(NSString *)container
{
    if (!filename) return NO;

    NSError *error;
    NSURL *localURL = [self localFileURL:filename];
    NSURL *ubiquityURL =
        [self ubiquityDocumentsFileURL:filename
            forContainer:container];

    BOOL localFound = [self isLocal:filename];
    BOOL ubiquityFound =
        [self isUbiquitousDocument:filename
            forContainer:container];

    // Check file not found
    if (!localFound && !ubiquityFound) return NO;

    // Check the two "nothing to be done" cases
    if (!yorn && localFound) return YES;
    if (yorn && ubiquityFound) return YES;

    // ubiquitous to local
    if (!yorn)
    {
        // Move file away from cloud
        if (![[NSFileManager defaultManager]
              setUbiquitous:NO
              itemAtURL:ubiquityURL
              destinationURL:localURL
              error:&error])
        {
            NSLog(@"Error removing %@ from %@ storage: %@",
                filename, container,
                error.localizedFailureReason);
            return NO;
        }
        return YES;
    }

    // local to ubiquitous
    if (![[NSFileManager defaultManager]
          setUbiquitous:YES
          itemAtURL:localURL
          destinationURL:ubiquityURL
          error:&error])
    {
        NSLog(@"Error moving %@ to %@ storage: %@",
            filename, container,
            error.localizedFailureReason);
        return NO;
    }
    return YES;
}

Deleting Files

These methods take three approaches to deleting files. The first deletes a local copy at the top of the Documents sandbox folder. The second moves items from the cloud (data and documents) and then deletes it locally. The third finds the file first and then invokes one of the previous solutions.

+ (BOOL) deleteLocal: (NSString *) filename
{
    NSURL *targetURL = [self localFileURL:filename];
    if (![[NSFileManager defaultManager]
        fileExistsAtPath:targetURL.path])
    {
        NSLog(@"Local file not found: %@", filename);
        return NO;
    }
    NSError *error;
    BOOL success = [[NSFileManager defaultManager]
        removeItemAtURL:targetURL error:&error];
    if (!success)
        NSLog(@"Error removing file %@: %@",
            filename, error.localizedFailureReason);

    return success;
}
+ (BOOL) deleteUbiquitousDocument:(NSString *)filename
    forContainer:(NSString *)container
{
    NSURL *targetURL =
        [self ubiquityDocumentsFileURL:filename
            forContainer:container];
    if (![[NSFileManager defaultManager]
        fileExistsAtPath:targetURL.path])
    {
        NSLog(@"Ubiquitous file not found: %@", filename);
        return NO;
    }

    // Remove from ubiquity and then delete
    BOOL success = [self setUbiquitous:NO
        for:filename forContainer:container];
    if (success)
        return [self deleteLocal:filename];
    return NO;
}
+ (BOOL) deleteUbiquitousData:(NSString *)filename
    forContainer:(NSString *)container
{
    NSError *error;
    BOOL success;

    NSURL *targetURL =
        [self ubiquityDataFileURL:filename
            forContainer:container];
    success = [[NSFileManager defaultManager]
        fileExistsAtPath:targetURL.path];
    if (!success)
    {
        NSLog(@"Ubiquitous file not found: %@", filename);
        return NO;
    }
    success = [[NSFileManager defaultManager]
        removeItemAtURL:targetURL error:&error];
    if (!success)
    {
        NSLog(@"Could not remove item at path: %@",
            error.localizedFailureReason);
        return NO;
    }
    return YES;
}
+ (BOOL) deleteDocument: (NSString *) filename
    forContainer:(NSString *)container
{
    // If local, delete it.
    if ([self isLocal:filename])
        return [self deleteLocal:filename];
    return [self deleteUbiquitousDocument:filename
        forContainer:container];
}

Retrieving Modification Dates

These methods retrieve the modification date for a file, in the top level of either Documents folder (sandbox or ubiquitous), depending on where the file is found.

+ (NSDate *) modificationDateForURL:(NSURL *) targetURL
{
    if (!targetURL) return nil;

    NSDictionary *attributes =

       [[NSFileManager defaultManager]
            attributesOfItemAtPath:targetURL.path error:nil];

   if (!attributes) return nil;

    return [attributes fileModificationDate];
}
+ (NSDate *) modificationDateForFile: (NSString *) filename
{
    if (!filename) return nil;
    return [self modificationDateForURL:
       [self fileURL:filename]];
}
+ (NSTimeInterval) timeIntervalSinceModification:
    (NSString *) filename
{
    return [[NSDate date] timeIntervalSinceDate:
       [self modificationDateForFile:filename]];
}
  • + Share This
  • 🔖 Save To Your Account