| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- import Foundation
- extension Disk {
- /// Create and returns a URL constructed from specified directory/path
- static func createURL(for path: String?, in directory: Directory) throws -> URL {
- let filePrefix = "file://"
- var validPath: String?
- if let path = path {
- do {
- validPath = try getValidFilePath(from: path)
- } catch {
- throw error
- }
- }
- var searchPathDirectory: FileManager.SearchPathDirectory
- switch directory {
- case .documents:
- searchPathDirectory = .documentDirectory
- case .caches:
- searchPathDirectory = .cachesDirectory
- case .applicationSupport:
- searchPathDirectory = .applicationSupportDirectory
- case .temporary:
- if var url = URL(string: NSTemporaryDirectory()) {
- if let validPath = validPath {
- url = url.appendingPathComponent(validPath, isDirectory: false)
- }
- if url.absoluteString.lowercased().prefix(filePrefix.count) != filePrefix {
- let fixedUrlString = filePrefix + url.absoluteString
- url = URL(string: fixedUrlString)!
- }
- return url
- } else {
- throw createError(
- .couldNotAccessTemporaryDirectory,
- description: "Could not create URL for \(directory.pathDescription)/\(validPath ?? "")",
- failureReason: "Could not get access to the application's temporary directory.",
- recoverySuggestion: "Use a different directory."
- )
- }
- case let .sharedContainer(appGroupName):
- if var url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName) {
- if let validPath = validPath {
- url = url.appendingPathComponent(validPath, isDirectory: false)
- }
- if url.absoluteString.lowercased().prefix(filePrefix.count) != filePrefix {
- let fixedUrl = filePrefix + url.absoluteString
- url = URL(string: fixedUrl)!
- }
- return url
- } else {
- throw createError(
- .couldNotAccessSharedContainer,
- description: "Could not create URL for \(directory.pathDescription)/\(validPath ?? "")",
- failureReason: "Could not get access to shared container with app group named \(appGroupName).",
- recoverySuggestion: "Check that the app-group name in the entitlement matches the string provided."
- )
- }
- }
- if var url = FileManager.default.urls(for: searchPathDirectory, in: .userDomainMask).first {
- if let validPath = validPath {
- url = url.appendingPathComponent(validPath, isDirectory: false)
- }
- if url.absoluteString.lowercased().prefix(filePrefix.count) != filePrefix {
- let fixedUrlString = filePrefix + url.absoluteString
- url = URL(string: fixedUrlString)!
- }
- return url
- } else {
- throw createError(
- .couldNotAccessUserDomainMask,
- description: "Could not create URL for \(directory.pathDescription)/\(validPath ?? "")",
- failureReason: "Could not get access to the file system's user domain mask.",
- recoverySuggestion: "Use a different directory."
- )
- }
- }
- /// Find an existing file's URL or throw an error if it doesn't exist
- static func getExistingFileURL(for path: String?, in directory: Directory) throws -> URL {
- do {
- let url = try createURL(for: path, in: directory)
- if FileManager.default.fileExists(atPath: url.path) {
- return url
- }
- throw createError(
- .noFileFound,
- description: "Could not find an existing file or folder at \(url.path).",
- failureReason: "There is no existing file or folder at \(url.path)",
- recoverySuggestion: "Check if a file or folder exists before trying to commit an operation on it."
- )
- } catch {
- throw error
- }
- }
- /// Convert a user generated name to a valid file name
- static func getValidFilePath(from originalString: String) throws -> String {
- var invalidCharacters = CharacterSet(charactersIn: ":")
- invalidCharacters.formUnion(.newlines)
- invalidCharacters.formUnion(.illegalCharacters)
- invalidCharacters.formUnion(.controlCharacters)
- let pathWithoutIllegalCharacters = originalString
- .components(separatedBy: invalidCharacters)
- .joined(separator: "")
- let validFileName = removeSlashesAtBeginning(of: pathWithoutIllegalCharacters)
- guard !validFileName.isEmpty, validFileName != "." else {
- throw createError(
- .invalidFileName,
- description: "\(originalString) is an invalid file name.",
- failureReason: "Cannot write/read a file with the name \(originalString) on disk.",
- recoverySuggestion: "Use another file name with alphanumeric characters."
- )
- }
- return validFileName
- }
- /// Helper method for getValidFilePath(from:) to remove all "/" at the beginning of a String
- static func removeSlashesAtBeginning(of string: String) -> String {
- var string = string
- if string.prefix(1) == "/" {
- string.remove(at: string.startIndex)
- }
- if string.prefix(1) == "/" {
- string = removeSlashesAtBeginning(of: string)
- }
- return string
- }
- /// Set 'isExcludedFromBackup' BOOL property of a file or directory in the file system
- static func setIsExcludedFromBackup(to isExcludedFromBackup: Bool, for path: String?, in directory: Directory) throws {
- do {
- let url = try getExistingFileURL(for: path, in: directory)
- var resourceUrl = url
- var resourceValues = URLResourceValues()
- resourceValues.isExcludedFromBackup = isExcludedFromBackup
- try resourceUrl.setResourceValues(resourceValues)
- } catch {
- throw error
- }
- }
- /// Set 'isExcludedFromBackup' BOOL property of a file or directory in the file system
- static func setIsExcludedFromBackup(to isExcludedFromBackup: Bool, for url: URL) throws {
- do {
- var resourceUrl = url
- var resourceValues = URLResourceValues()
- resourceValues.isExcludedFromBackup = isExcludedFromBackup
- try resourceUrl.setResourceValues(resourceValues)
- } catch {
- throw error
- }
- }
- /// Create necessary sub folders before creating a file
- static func createSubfoldersBeforeCreatingFile(at url: URL) throws {
- do {
- let subfolderUrl = url.deletingLastPathComponent()
- var subfolderExists = false
- var isDirectory: ObjCBool = false
- if FileManager.default.fileExists(atPath: subfolderUrl.path, isDirectory: &isDirectory) {
- if isDirectory.boolValue {
- subfolderExists = true
- }
- }
- if !subfolderExists {
- try FileManager.default.createDirectory(at: subfolderUrl, withIntermediateDirectories: true, attributes: nil)
- }
- } catch {
- throw error
- }
- }
- /// Get Int from a file name
- static func fileNameInt(_ url: URL) -> Int? {
- let fileExtension = url.pathExtension
- let filePath = url.lastPathComponent
- let fileName = filePath.replacingOccurrences(of: fileExtension, with: "")
- return Int(String(fileName.filter { "0123456789".contains($0) }))
- }
- }
|