【问题标题】:iOS file path is changing at every launch/rerun the application每次启动/重新运行应用程序时,iOS 文件路径都会发生变化
【发布时间】:2020-12-04 23:52:11
【问题描述】:

我在每次启动应用程序时更改文件路径时遇到问题。 我在应用程序包中有一个文件(“AppConstant.json”),我需要将此文件复制到应用程序文档目录中。我已成功将“AppConstant.json”文件保存在 Document 目录上创建的用户文件夹“MyFolder”中。

但问题是当我第二次重新启动应用程序时,它没有显示相同的路径。我也在使用相对路径,但仍然没有得到。

这里是代码 // 调用目录

let stringAppConstant = copyFileFromBundleToDocumentDirectory(resourceFile: "AppConstant", resourceExtension: "json")

//保存或获取退出文件路径

func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) -> String 
  {
        var stringURLPath = "Error_URLPath"
        let fileManager = FileManager.default
        let docURL = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
        let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
        let fileName = "\(resourceFile).\(resourceExtension)"
        guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
            return stringURLPath
        }

       if !fileManager.fileExists(atPath: newDestPath.path) {
            do {
                try fileManager.createDirectory(atPath: newDestPath.path,withIntermediateDirectories: true, attributes: nil)
                print("Created folder successfully in :::", newDestPath.path)
            } catch {
                print("Error in creating folder :::",error.localizedDescription);
            }
        }
        else {
            print("Folder is already exist!")
        }
        if fileManager.fileExists(atPath: fullDestPath.path) {
            print("File is exist in ::: \(fullDestPath.path)")
            stringURLPath = fullDestPath.path
        }
        else {
            do {
                try fileManager.copyItem(atPath:  sourcePath, toPath: fullDestPath.path)
                print("Saved file successfully in :::", fullDestPath.path)
                stringURLPath = fullDestPath.path
            } catch {
                print("Error in creating file ::: \(error.localizedDescription)")
            }
        }
        return stringURLPath
    }

请帮助我,我需要在 Sandbox 中保存路径。这是我实施的正确方式吗?

我在设备和模拟器中运行,重新启动时两条路径不同 这是首次启动的路径: /var/mobile/Containers/Data/Application/81B568A7-0932-4C3E-91EB-9DD62416DFE8/Documents/MyFolder/AppConstant.json

重新启动我正在获取新路径的应用程序: /var/mobile/Containers/Data/Application/3DAABAC3-0DF5-415B-82A5-72B204311904/Documents/MyFolder/AppConstant.json

注意:我创建了一个示例项目,并使用了相同的代码并且它正在工作。但在现有项目中它不起作用。我仅对示例和项目使用相同的包 ID 和配置文件。检查文件添加参考,设置,版本都一样。

有什么想法吗?

【问题讨论】:

  • 帮助缩小范围的问题 - 当您说“未显示相同的路径”时,您是指 fullDestPath 吗?另外,您是在模拟器中运行,还是在实际设备上运行?当您“再次启动”时,您是以相同的方式启动还是以不同的方式启动(例如从 Xcode 调试一次启动而不是另一次启动)?
  • 嗨@Corbell,我更新了我的问题。我同时启动了设备和模拟器,它们的行为也相同。下次我启动相同的方式来调用该方法时
  • copyFileFromBundleToDocumentDirectory的返回值怎么办?如果您将其存储以稍后访问该文件,则可能会出现问题,因为它包含绝对路径而不是相对路径。
  • 嗨@GlebA.,在启动应用程序时(第一次启动-第一次安装)我将文件保存在文档目录中,之后我需要使用相同的文件(已经保存在文档目录中的文件)以供进一步使用。那么我怎样才能得到“相对路径”。请指导我
  • 我猜这个说法是错误的let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)") 可以换成let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: resourceExtension)

标签: ios swift file-manager


【解决方案1】:

容器路径周期性变化的行为是正常的。

这些行

let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
    return stringURLPath
}

包含很多错误

  • URL(string 是错误的文件路径 API,它是 URL(fileURLWithPath)
  • path(forResource:ofType:) 的第二个参数不能有前导点。
  • API absoluteString 错误作为URL(fileURLWithPath 的参数
  • 不是真正的错误,但不要在 Swift 中使用 NSURL

强烈建议始终使用URL 相关的API 来连接路径并从FileManager 获取文档文件夹。此外,最好使方法throw 成为真正的错误,而不是返回无意义的文字字符串。而NSSearchPathForDirectoriesInDomains 已经过时,不应该在 Swift 中使用。

func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) throws -> URL
{
    let sourceURL = Bundle.main.url(forResource: resourceFile, withExtension: resourceExtension)!
    
    let fileManager = FileManager.default
    let destFolderURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("MyFolder")
    let fullDestURL = destFolderURL.appendingPathComponent(resourceFile).appendingPathExtension(resourceExtension)
    
    if !fileManager.fileExists(atPath: destFolderURL.path) {
        try fileManager.createDirectory(at: destFolderURL, withIntermediateDirectories: true, attributes: nil)
        print("Created folder successfully in :::", destFolderURL.path)
        try fileManager.copyItem(at: sourceURL, to: fullDestURL)
        print("Saved file successfully in :::", fullDestURL.path)
    } else {
        print("Folder already exists!")
        if fileManager.fileExists(atPath: fullDestURL.path) {
            print("File exists in ::: \(fullDestURL.path)")
        } else {
            try fileManager.copyItem(at: sourceURL, to: fullDestURL)
            print("Saved file successfully in :::", fullDestURL.path)
        }
    }
    return fullDestURL
}

【讨论】:

  • 嗨@vadian,现在我也遇到同样的错误,每次它创建一个新的路径和文件。同时在我创建的示例项目中,它现在正在工作。我给出了相同的包 ID、应用程序名称和配置文件也相同。我想添加任何其他的
  • 我认为 App Sandbox 帐户或设置路径中存在一些问题 - 不确定,因为在新的示例测试项目中相同的代码正在工作(由您提供)
  • 容器路径(UUID)改变了,但是容器的内容不能改变。永远不要保存完整路径,始终使用带有.documentDirectory的相对路径@
  • 嗨@vadian,我希望问题出在其他地方,因为相同的代码在示例项目中工作。我真的需要找到。使用相对路径也不起作用。谢谢
【解决方案2】:

编辑 1:

您好,我创建了新项目并使用了我在 main 中发布的相同代码,并且它正在运行。但在实际项目中它不起作用。

不确定您的项目到底发生了什么,请尝试调试它。这也是发展的一部分。 :)

如果你在这个周末急于解决这个问题,请尝试使用以下代码 sn-p。

// collect data from bundle
let constFileURL = Bundle.main.url(forResource: "AppConst", withExtension: "json")!
let data = try! Data(contentsOf: constFileURL)

// try to write data in document directory
do {
    let constFileURL = try saveFileInDocumentDirectory(filePath: "MyFolder/AppConst.json", data: data)
    // use your `constFileURL`
} catch (let error as FileOperationError) {
    switch error {
    case .fileAlreadyExists(let url):
        let data = try! Data(contentsOf: url)
        print(String(data: data, encoding: .utf8))
    case .IOError(let error):
        print("IO Error \(error)")
    }
} catch {
    print("Unknown Error \(error)")
}

// Helpers
enum FileOperationError: Error {
    case fileAlreadyExists(url: URL)
    case IOError(Error)
}
func saveFileInDocumentDirectory(filePath: String, data: Data) throws -> URL {
    
    // final destination path
    let destURLPath = fullURLPathOf(filePath, relativeTo: .documentDirectory)
    // check for file's existance and throw error if found
    guard FileManager.default.fileExists(atPath: destURLPath.path) == false else {
        throw FileOperationError.fileAlreadyExists(url: destURLPath)
    }
    // Create Intermidiate Folders
    let intermidiateDicPath = destURLPath.deletingLastPathComponent()
    if FileManager.default.fileExists(atPath: intermidiateDicPath.path) == false {
        do {
            try FileManager.default.createDirectory(at: intermidiateDicPath, withIntermediateDirectories: true, attributes: nil)
        } catch {
            throw FileOperationError.IOError(error)
        }
    }
    
    // File Writing
    do {
        try data.write(to: destURLPath, options: .atomic)
    } catch {
        throw FileOperationError.IOError(error)
    }
    return destURLPath
}
func fullURLPathOf(_ relativePath: String, relativeTo dic:FileManager.SearchPathDirectory ) -> URL {
    return FileManager.default.urls(for: dic, in: .userDomainMask).first!.appendingPathComponent(relativePath)
}

原答案

文件操作成功后为什么不直接返回"MyFolder/\(fileName)"?如果您以后需要访问该路径,您始终可以使用 FileManager API 来完成。

let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let constFilePath = docDir.appendingPathComponent("MyFolder/\(fileName)")

// Access const file data
do { 
  let fileData = try Data(contentsOf: constFilePath)

  // Use you data for any further checking

} catch {
  // Error in reading file data
  print("Error in file data access : \(error)")
}

【讨论】:

  • 嗨无论如何要解决这个问题,现在每次启动它都会创建为新路径,我需要如果已经有文件,那么我需要确切的路径,不想重新创建文件夹和文件.任何想法
  • 嗨@Sauvik,由于路径中的文件不可用,我总是在 fileData (let fileData = try?Data(contentsOf: constFilePath)) 中得到 nil
  • @Prasanth 只需使用do-try-catch 检查编辑后的代码 sn-p,你能检查你得到的错误吗?
  • 您好,我收到文件“AppConstant.json”无法打开,因为没有这样的文件
  • 我犯了一个错误,我没有使用.documentDirectory,而是使用了.applicationSupportDirectory。现在可以试试吗?
猜你喜欢
  • 2012-05-18
  • 1970-01-01
  • 2021-03-18
  • 1970-01-01
  • 2021-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-24
相关资源
最近更新 更多