8

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?

4

1 回答 1

4

你根本无法规避这个错误,我很确定。我没有找到任何官方文档 - 但有几个教程解释了如何允许在越狱设备上使用符号链接。我没有理由为什么 iOS 上的安全性与硬链接有任何不同。在模拟器中这是可行的,因为它在本地机器上的路径上运行(在 ~/Library/Application Support/iphone Simulator/iOSVersion/Applications/...)并且具有本地安全性。作为一个反例,我不止一次想到我在模拟器中工作的东西在设备上不起作用,反之亦然(例如,当尝试在主包中打开带有 SQLITE_WRITE 标志的 sqlite3 db 时) .

我通过使用两个不同的文件位置解决了我的应用程序中的这个资源管理/版本控制问题。原始版本像往常一样位于主包中。另一个位置是缓存目录或文档目录。然后我使用一个自定义资源处理程序,它返回一个提供的相对路径的完整路径。它的工作原理就像这样:

  • 如果可写目录中存在相对路径,则返回该路径。这应该是请求的相对文件路径的更新版本
  • 如果之前的检查失败,在主包目录中查找它以从应用程序的主包中返回原始版本

一些代码(使用缓存目录作为可更新目录,您可能需要更改它):

#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;
}

然后,在您的代码中,您将使用 UpdateableCacheResourcePath("some.file") 而不是查看主包。它也适用于引用文件夹(将文件夹拖放到 xcode 并选择创建引用而不是组)。然后,您可以引用父文件夹的相对路径(如 ads/adbanner.png)。

唯一的缺点是,如果您有 Web 内容,则出于显而易见的原因需要更新您的 html 站点引用的所有文件(Web 浏览器只能遵循指向 html 文档所在文件夹的相对路径)。

于 2014-11-28T08:30:39.270 回答