简体   繁体   中英

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.

How can I access a .txt file included in my app using Swift on an actual iPhone ? 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

EDIT - 28 August 2018

This is how to do it in 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 .

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:

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:

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.

Get File From Bundle in 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 .

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

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")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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