简体   繁体   English

如何访问 Swift 中应用程序包中包含的文件?

[英]How to access file included in app bundle in Swift?

I know there are a few questions pertaining to this, but they're in Objective-C.我知道有几个问题与此有关,但它们在 Objective-C 中。

How can I access a .txt file included in my app using Swift on an actual iPhone ?如何在实际 iPhone 上使用 Swift 访问我的应用程序中包含的.txt文件? I want to be able to read and write from it.我希望能够从中读取和写入。 Here are my project files if you want to take a look.如果您想看一下,是我的项目文件。 I'm happy to add details if necessary.如有必要,我很乐意添加详细信息。

Simply by searching in the app bundle for the resource只需在应用程序包中搜索资源

var filePath = NSBundle.mainBundle().URLForResource("file", withExtension: "txt")

However you can't write to it because it is in the app resources directory and you have to create it in the document directory to write to it但是你不能写入它,因为它在应用程序资源目录中,你必须在文档目录中创建它才能写入它

var documentsDirectory: NSURL?
var fileURL: NSURL?

documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last!
fileURL = documentsDirectory!.URLByAppendingPathComponent("file.txt")

if (fileURL!.checkResourceIsReachableAndReturnError(nil)) {
    print("file exist")
}else{
    print("file doesnt exist")
    NSData().writeToURL(fileURL!,atomically:true)
}

now you can access it from fileURL现在您可以从fileURL访问它

EDIT - 28 August 2018编辑 - 2018 年 8 月 28 日

This is how to do it in Swift 4.2这是在Swift 4.2 中的方法

var filePath = Bundle.main.url(forResource: "file", withExtension: "txt")

To create it in the document directory在文档目录中创建它

if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last {
   let fileURL = documentsDirectory.appendingPathComponent("file.txt")
   do {
       if try fileURL.checkResourceIsReachable() {
           print("file exist")
       } else {
           print("file doesnt exist")
           do {
            try Data().write(to: fileURL)
           } catch {
               print("an error happened while creating the file")
           }
       }
   } catch {
       print("an error happened while checking for the file")
   }
}

Swift 3 , based on Karim's answer . Swift 3 ,基于Karim 的回答

Reading

You can read files included in an app's bundle through the bundle's resource:您可以通过捆绑包的资源读取应用程序捆绑包中包含的文件:

let fileURL = Bundle.main.url(forResource:"filename", withExtension: "txt")

Writing写作

However, you can't write there.但是,你不能在那里写。 You will need to create a copy, preferably in the Documents directory:您需要创建一个副本,最好在 Documents 目录中:

func makeWritableCopy(named destFileName: String, ofResourceFile originalFileName: String) throws -> URL {
    // Get Documents directory in app bundle
    guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
        fatalError("No document directory found in application bundle.")
    }

    // Get URL for dest file (in Documents directory)
    let writableFileURL = documentsDirectory.appendingPathComponent(destFileName)

    // If dest file doesn’t exist yet
    if (try? writableFileURL.checkResourceIsReachable()) == nil {
        // Get original (unwritable) file’s URL
        guard let originalFileURL = Bundle.main.url(forResource: originalFileName, withExtension: nil) else {
            fatalError("Cannot find original file “\(originalFileName)” in application bundle’s resources.")
        }

        // Get original file’s contents
        let originalContents = try Data(contentsOf: originalFileURL)

        // Write original file’s contents to dest file
        try originalContents.write(to: writableFileURL, options: .atomic)
        print("Made a writable copy of file “\(originalFileName)” in “\(documentsDirectory)\\\(destFileName)”.")

    } else { // Dest file already exists
        // Print dest file contents
        let contents = try String(contentsOf: writableFileURL, encoding: String.Encoding.utf8)
        print("File “\(destFileName)” already exists in “\(documentsDirectory)”.\nContents:\n\(contents)")
    }

    // Return dest file URL
    return writableFileURL
}

Example usage:用法示例:

let stuffFileURL = try makeWritableCopy(named: "Stuff.txt", ofResourceFile: "Stuff.txt")
try "New contents".write(to: stuffFileURL, atomically: true, encoding: String.Encoding.utf8)

Just a quick update for using this code with Swift 4:只是在 Swift 4 中使用此代码的快速更新:

Bundle.main.url(forResource:"YourFile", withExtension: "FileExtension")

And the following has been updated to account for writing the file out:并且以下已更新以说明写出文件:

var myData: Data!

func checkFile() {
    if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last {
        let fileURL = documentsDirectory.appendingPathComponent("YourFile.extension")
        do {
            let fileExists = try fileURL.checkResourceIsReachable()
            if fileExists {
                print("File exists")
            } else {
                print("File does not exist, create it")
                writeFile(fileURL: fileURL)
            }
        } catch {
            print(error.localizedDescription)
        }
    }
}

func writeFile(fileURL: URL) {
    do {
        try myData.write(to: fileURL)
    } catch {
        print(error.localizedDescription)
    }
}

This particular example is not the most flexible, but with a little bit of work you can easily pass in your own file names, extensions and data values.这个特定的例子并不是最灵活的,但是通过一些工作,您可以轻松地传递您自己的文件名、扩展名和数据值。

Bundles are read only.捆绑包是只读的。 You can use NSBundle.mainBundle().pathForResource to access the file as read-only, but for read-write access you need to copy your document to Documents folder or tmp folder.您可以使用NSBundle.mainBundle().pathForResource以只读方式访问文件,但对于读写访问,您需要将文档复制到 Documents 文件夹或 tmp 文件夹。

Get File From Bundle in Swift 5.1在 Swift 5.1 中从包中获取文件

//For Video File
let stringPath = Bundle.main.path(forResource: "(Your video file name)", ofType: "mov")

let urlVideo = Bundle.main.url(forResource: "Your video file name", withExtension: "mov")

🎁 Property Wrapper - Fetch and convert to correct data type 🎁 属性包装器 - 获取并转换为正确的数据类型

This simple wrapper helps you to load any file from any bundle in a cleanest way:这个简单的包装器可以帮助您以最干净的方式从任何包中加载任何文件:

@propertyWrapper struct BundleFile<DataType> {
    let name: String
    let type: String
    let fileManager: FileManager = .default
    let bundle: Bundle = .main
    let decoder: (Data) -> DataType

    var wrappedValue: DataType {
        guard let path = bundle.path(forResource: name, ofType: type) else { fatalError("Resource not found: \(name).\(type)") }
        guard let data = fileManager.contents(atPath: path) else { fatalError("Can not load file at: \(path)") }
        return decoder(data)
    }
}

Usage:用法:

@BundleFile(name: "avatar", type: "jpg", decoder: { UIImage(data: $0)! } )
var avatar: UIImage

You can define any decoder to match your needs您可以定义任何解码器来满足您的需求

Bundles can be written.可以写包。 You can use Bundle.main.path to overwrite file by adding it into Copy Bundles Resource .您可以使用Bundle.main.path通过将文件添加到Copy Bundles Resource来覆盖文件。

项目 -> 目标 -> 构建阶段 -> 复制捆绑资源

I have to use a file from another bundle.我必须使用另一个包中的文件。 So, following code worked for me.因此,以下代码对我有用。 Needful when you work with a frameworks.当你使用框架时是必需的。

let bundle = Bundle(for: ViewController.self)
let fileName = bundle.path(forResource: "fileName", ofType: "json")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM