Search code examples
iosobjective-cswiftnsurlnsfilemanager

How to retrieve nested file with FileManager?


In my app, I create this folder structure as soon as the app launches for the first time:

enter image description here

I read here Can I save the keep the absolute path of a file in database? - that I should not store the absolute path to a directory or file. Therefore I am having trouble getting reference to the last file shown in the picture above called I-Want-This-File-Path.

I could get access up to the Feed folder in the picture above like so:

extension FileManager {
    /// APPLICATION SUPPORT DIRECTORY
    static func createOrFindApplicationSupportDirectory() -> URL? {
        let bundleID = Bundle.main.bundleIdentifier
        // Find the application support directory in the home directory.
        let appSupportDir = self.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)

        guard appSupportDir.count > 0 else {
            return nil
        }

        // Append the bundle ID to the URL for the Application Support directory.
        let dirPath = appSupportDir[0].appendingPathComponent(bundleID!)

        // If the directory does not exist, this method creates it.
        do {
            try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
            return dirPath
        } catch let error {
            print("Error creating Application Support directory with error: \(error)")
            return nil
        }
    }

    /// FEED DIRECTORY
    static func createOrFindFeedDirectory() -> URL? {
        guard let appSupportDir = createOrFindApplicationSupportDirectory() else {
            return nil
        }

        let dirPath = appSupportDir.appendingPathComponent("Feed")

        // If the directory does not exist, this method creates it.
        do {
            try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
            return dirPath
        } catch let error {
            print("Error creating Favorites directory with error: \(error)")
            return nil
        }
    }
}

To get access to the FileStack folder I have an object called FileStack which saves part of the path. Instead of saving the absolute path, I build it at runtime like so:

class FileStack {

    var title = ""
    var localDirectoryName: String?
    var files: File? // THIS OBJECT IS WHERE I WANT TO SAVE THE PATH

    // THIS IS HOW I BUILD THE URL AT RUNTIME TO GET THE PATH TO THE DIRECTORY CALLED "FILESTACK"
    var localDirectoryPath: URL? {
        guard let localDirectoryName = localDirectoryName else { return nil }
        return FileManager.createOrFindFeedDirectory()?.appendingPathComponent(localDirectoryName)
    }
}

Notice the property var files: File?- this is a custom object called File where I want to save the path to I-Want-This-File-Path file in the picture above. THe object is like so:

class File {

    dynamic var iWantThisFileName: String?

    var iWantThisFilePath: URL? {
        guard let iWantThisFileName = iWantThisFileName else { return nil }
        return /// ??? HOW DO I RETURN THE PATH HERE?
    }
}

So ultimately I want to be able to get the path of I-Want-This-File-Path like so:

let fileStack = FileStack()
fileStack.file.iWantThisFilePath // This will give me the path

Any suggestions?

Update 1

In the Application Support -> com.Company.DemoApp directory, there will be multiple folders. For example, a Feed and SavedForLater as shown here. Also FileStack will have multiple files as shown here.

enter image description here

Update 2

In other words, I am having trouble building out the path for the File object at runtime.

I need to pass the FileStack's localDirectoryName to it's nested object File because the File object will need that to build out it's path at runtime. I have the code above which shows something like this. If these were separate objects, meaning not nested within each other, I could simply pass a url to the next object... but since they are nested, I'm stuck.



Solution

  • I think that you misunderstood the answer in other question. You want to get this path:

    /Library/Application Support/com.Company.DemoApp/Feed/FileStack/I-Want-This-File
    

    /Library/Application Support/ this is the part you can't save anywhere and you have to get it during runtime from FileManager. This is because you have no control over this path and it can change without your approval. You want paths in your application to be relative to this folder.

    com.Company.DemoApp/Feed/FileStack/I-Want-This-File this is part of that file path that you can store in database/config files. This path will change only when you change it. So If you update your application and change paths you can also change paths in database.

    This is rough example how it can be implemented:

    enum DataDirectoryType : String
        {
        case wantedFile = "Feed/FileStack/I-Want-This-File-Path"
        case anotherFiles = "Feed/AnotherFiles/"
    }
    
    extension FileManager {
        /// APPLICATION SUPPORT DIRECTORY
        static private func createOrFindApplicationSupportDirectory() -> URL? {
            let bundleID = Bundle.main.bundleIdentifier
            // Find the application support directory in the home directory.
            let appSupportDir = self.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
    
            guard appSupportDir.count > 0 else {
                return nil
            }
    
            // Append the bundle ID to the URL for the Application Support directory.
            return appSupportDir[0].appendingPathComponent(bundleID!)
        }
    
        static func createOrFindFilePath(_ type : DataDirectoryType ) -> URL? {
            guard let appSupportDir = createOrFindApplicationSupportDirectory() else {
                return nil
            }
    
            let dirPath = appSupportDir.appendingPathComponent(type.rawValue)
    
            // If the directory does not exist, this method creates it.
            do {
                try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
                return dirPath
            } catch let error {
                print("Error creating Favorites directory with error: \(error)")
                return nil
            }
        }
    }
    
    
    let urlToWantedFile = FileManager.createOrFindFilePath(.wantedFile)