Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create hard links to files in an iOS application bundle?

In order to save on server side bandwidth costs associated with my iOS application, I've packaged a bunch of assets that would otherwise be downloadable at runtime into my iOS application bundle. In the context of the application as written, it would be ideal for me if I could access the files from one of the user writeable directories (e.g. [App Dir]/Library/Application Support/My Custom Subfolder/) without having to copy the files there directly at runtime (e.g. at startup, first run, whatever).

While I've been able to successfully create symbolic links in .../My Custom Subfolder/ to the files in the bundle using the NSFileManager API createSymbolicLinkAtURL:withDestinationURL:error:, some of the framework APIs I then use to access the content later on get goofed up and give me back attributes and data pertaining to the symbolic links instead of the underlying file. I could probably mitigate those issues by utilizing some other framework APIs but it might end up being a lot of work depending on the scope of the incorrect usages.

On the simulator I was able to successfully circumvent this issue by creating hard links to the bundle content using the NSFileManager API linkItemAtURL:toURL:error:. The hard links worked great for all the file access APIs utilized throughout the app and everything was peachy. On DEVICE however (tested on iPhone 5c running iOS 7.0.2 and iPad running iOS 7.1), I would receive an NSCocoaErrorDomain 513 error (Operation could not be completed. Operation not permitted.). I could create a test file in .../My Custom Subfolder/ and create a hard link to that in the same folder just fine, but if I try to hardlink to anything in the read-only application bundle, I get the 513 error.

Does anyone know if there's a way to get around the permissions error in order to accomplish what I'm trying to do?

like image 563
zemelenda Avatar asked Apr 11 '14 15:04

zemelenda


People also ask

What is a hard link to a file?

A hard link is the file system representation of a file by which more than one path references a single file in the same volume.

Can you create multiple hard links to the same file?

Because hard links point to an inode, and inodes are only unique within a particular file system, hard links cannot cross file systems. If a file has multiple hard links, the file is deleted only when the last link pointing to the inode is deleted and the link count goes to 0.

How do you create a hard link on a Mac?

Press Command+Space, type “Terminal”, and then press “Enter” to open Terminal from Spotlight search. Navigate to Finder > Applications > Utilities > Terminal to launch the Terminal shortcut. The -s here tells the ln command to create a symbolic link. If you want to create a hard link, you'd omit the -s .

How do I link to an iOS app?

Find app link via App Store on iOS devicesStep 1: Go to the Apple App Store on your iOS device. Step 2: Search for your app and go to the app page. Step 3: Tap the Share Sheet button on the top right of the screen, then choose Copy Link.


1 Answers

You simply can't circumvent this error, I'm sure about that. I did not find any official docuentation - but several tutorials explaining how to allow the usage of symbolic links on jailbroken devices. There is no reason to me why the security on iOS is any different for hard links. In the simulator this works because it is operating on a path on your local machine (in ~/Library/Application Support/iphone Simulator/iOSVersion/Applications/...) and with local security. As an counter example, it occurred to me more than once that I had something working in the simulator that didn't work on the device and vice versa (for an example when trying to open a sqlite3 db in the main bundle with SQLITE_WRITE flag).

I resolve this resource management / versioning issue in my apps by using two different file locations. The original versions are inside the main bundle as usual. The other location is either the cache dir or the documents dir. Then I use a custom resource handler which returns a full path for a provided relative path. It works simply like so:

  • If a relative path exists in the writeable dir, return this one. This is supposed to be an updated version of the requested relative file path
  • If the previous check fails, look it up in the main bundle directory to return the original version from the app's main bundle

Some code (using the cache dir as the updateable directory, you may want to change that):

#define UpdateableCacheResourcePath(path) \
[[ResourceHandler sharedManager] updateableCacheResourcePath:path]

- (NSString*)updateableCacheResourcePath:(NSString *)path
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES);
    NSString *_libraryCacheStorageDir = [[paths objectAtIndex:0] copy];
    NSString *updateablePath = [_libraryCacheStorageDir stringByAppendingPathComponent:path];
    // Cache Dir
    if ([_fileManager fileExistsAtPath:updateablePath]) {
        return updateablePath;
    }

    // mainBundle
    NSString *mainbundleResourcePath = [_mainBundlePath stringByAppendingPathComponent:path];
    if ([_fileManager fileExistsAtPath:mainbundleResourcePath]) {
        return mainbundleResourcePath;
    }

    NSLog(@"File not found: %@", path);
    return nil;
}

In your code you would then use UpdateableCacheResourcePath("some.file") instead of looking in the main bundle. It also works with refrenced folders (drag drop folder to xcode and choose to create a reference instead of a group). Then you can reference relative paths with parent folders (like advertisements/adbanner.png).

The only drawback is that if you have web content you need to update all files that are referenced by your html site for obvious reasons (the web browser can only follow relative paths to the folder where the html document is inside).

like image 124
benjist Avatar answered Sep 19 '22 13:09

benjist