Furl – basic file URL operations

Why?

I found myself re-writing common file/folder access/info routines. While the Cocoa apis are very capable, sometimes getting basic information is quite tedious.

This is a micro-framework of basic file/folder and Spotlight query operations.

Basic example

// Create a temporary folder to work in
let tempFolder = Folder.Temporary(create: true)

// Generate a new file within the folder, but don't create it
let workingFile = try tempFolder.file("output.txt")

// Write some text into the file
try "hello".write(to: workingFile.fileURL, atomically: true, encoding: .utf8)

// Check that we are a file
assert(workingFile.isFile)

// Grab the file's UTI
let uti = try workingFile.typeIdentifier()  // should be "public.plain-text"

// Lock the file
workingFile.isLocked = true

// Generate a child folder (note, this by default does not create it)
let childFolder = try tempFolder.subFolder("child")

// Make the folder on disk. Note this creates the folder if it doesn't already exist
try childFolder.actualize()

// And finally remove our temporary folder
try tempFolder.delete()

API

Location

A Location is the common sub-unit of a File and a Folder. It contains routines that are common to both files and folders.

You never explicitly create a Location, rather you will work with File and Folder.

Attributes

API Description
basename The location’s name including the extension
name The location’s name without the extension
displayName The location’s display name
extension The location’s extension
parent The location’s containing folder
state The location’s state (folder, file, unknown)
exists Does the location exist on disk?
doesNotExist Does the location not yet exist on disk?
isFolder Is this an existing folder?
isFile Is this an existing file?
isAlias Is this an alias file?
isSymlink Is this a symbolic link?
creationDate The location’s creation date
modificationDate The location’s modification date
isExtensionHidden Is the location’s extension hidden? (read/write)
isLocked Is the location locked? (read/write)
attributes The location’s attributes

Basic permissions

API Description
isReadable The location can be read
isWritable The location can be written to
isExecutable The location can be executed
isDeletable The location is deletable

Universal Type Identifier

API Description
contentType() The UTI for the location (if it exists)
typeIdentifier() The type identifier for the location
conformsTo() Does this location conform to the specified UTI

Operations

API Description Notes
move() Move the file/folder to a new location
copy() Copy the file/folder to a new location
rename() Rename the file/folder
delete() Remove the file/folder from disk
moveToTrash() Move the file/folder to the trash macOS only
revealInFinder() Reveal the file/folder in the Finder macOS only

Symlinks and Aliases

API Description Notes
resolvingSymLinks() Resolve any symlinks within the location
createSymLink() Create a symlink
resolvingAlias() Resolves the destination of the alias file macOS only
createAlias() Create a file/folder alias macOS only

File

A File object represents a file. The file may or may exist yet.

let file = File(fileURL: <some url>)
guard file.exists else { .... }
let fileSize = file.fileSize
let modificationDate = file.modificationDate
let uti = try file.contentType()

try file.moveToTrash()

General

API Description
fileSize The file’s size in bytes
standardized Returns a File with a standardised file path
actualize() If the file doesn’t exist, create the file on disk

Temporary files

API Description
File.Temporary() Returns a temporary file

Folder

A Folder object represents a folder. The representation may or may not yet exist on disk

Temporary folders

API Description
Folder.Temporary() Returns a temporary folder
createUniqueFile() Create a unique file within this folder
createUniqueSubfolder() Create a unique subfolder within this folder
createUniqueDatedSubfolder() Create a unique subfolder within this folder of the form <identifier>/<date>

Locating files

API Description
contains() Does this folder contain a location with the specified name
containsFolder() Does this folder contain a subfolder with the specified name
containsFile() Does this folder contain a file with the specified name

Generating files/folders

API Description
subfolder() A subfolder in this folder with a specified name
file() A file in this folder with a specified name
writeDataToFile() Write data to a file in this folder

Folder Content

API Description Notes
isEmpty() Is this folder empty?
enumerateContent() Enumerate the contents of this folder Optionally recursive
allContent() Returns all the locations in this folder Optionally recursive
allSubfolders() Returns just the subfolders of this folder Optionally recursive
allFiles() Returns just the files contained in this folder Optionally recursive

Common folder locations

API Description Notes
Folder.current() The process’ current working folder
Folder.userHomeFolder() User’s home folder
Folder.userDocumentsFolder() User’s documents folder
Folder.userDesktopFolder() User’s desktop folder
Folder.userCachesFolder() User’s caches folder
Folder.userDownloadsFolder() User’s downloads folder
Folder.userLibraryFolder() User’s library folder
Folder.userTemporaryFolder() User’s temporary folder
Folder.userTrashFolder() User’s trash folder macOS only

Location Query (macOS only)

LocationQuery is a basic wrapper around Spotlight search with a callback syntax

let q = LocationQuery()
q.searchScopes = [try Folder.userLibraryFolder()]
q.predicate = NSPredicate(format: "%K ENDSWITH '.txt'", NSMetadataItemFSNameKey)
q.start { foundItems in 
	// `foundItems` is an array of found metadata items
}
API Description
searchScopes The search scopes for the query (URL, String or Folder)
predicate The query predicate (Syntax)
start() Start the query, providing a completion handler to receive the results
stop() Stop a running query

License

MIT License

Copyright (c) 2023 Darren Ford

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

GitHub

View Github