A Swift library to calculate file hashes.
Add the package in Xcode (File → Add Packages…) or in Package.swift:
dependencies: [
.package(url: "https://github.com/CrazyFanFan/FileHash.git", exact: "0.0.1")
]import FileHash
let path = "/path/to/file"
let md5 = FileHasher.md5(ofFileAtPath: path)
let sha1 = FileHasher.sha1(ofFileAtPath: path)
let sha256 = FileHasher.hash(.sha256, ofFileAtPath: path)All APIs accept String?. Passing nil returns nil.
Hashing reads file content in chunks. You can change the chunk size per call:
let md5 = FileHasher.md5(ofFileAtPath: path, chunkSize: .kb(256))
let sha512 = FileHasher.sha512(ofFileAtPath: path, chunkSize: .mb(1))The chunk size is clamped into a safe range:
ChunkSize.minChunkSize(.kb(1))ChunkSize.maxChunkSize(.mb(1))
Available on iOS 13 / macOS 10.15 / watchOS 6 / tvOS 13 and newer.
@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *)
func example(path: String) async {
let md5 = await FileHasher.md5(ofFileAtPath: path, chunkSize: .kb(64))
let sha256 = await FileHasher.hash(.sha256, ofFileAtPath: path, chunkSize: .kb(64))
_ = (md5, sha256)
}Hash multiple file paths while keeping the same ordering as input:
let paths: [String?] = ["/a.txt", "/b.txt", nil]
let md5s = FileHasher.hash(.md5, ofFileAtPaths: paths, chunkSize: .kb(64))
let pairs = FileHasher.hashPairs(.sha256, ofFileAtPaths: paths, chunkSize: .kb(64))Async batch hashing supports limiting concurrency:
@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *)
func batchExample(paths: [String?]) async {
let results = await FileHasher.hash(.md5, ofFileAtPaths: paths, chunkSize: .kb(64), maxConcurrentTasks: 4)
_ = results
}If you want progress updates (e.g. update UI as each file completes), use the stream API.
The stream yields (index, path, hash); completion order may differ from input order.
@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *)
func streamExample(paths: [String?]) async {
for await item in FileHasher.hashStream(.md5, ofFileAtPaths: paths, chunkSize: .kb(64), maxConcurrentTasks: 4) {
print(item.index, item.path ?? "nil", item.hash ?? "nil")
}
}Stopping iteration cancels the underlying producer task, which helps avoid wasted work.
If you used the old Hasher name, it is now deprecated and renamed to FileHasher.
@available(*, deprecated, renamed: "FileHasher")
public typealias Hasher = FileHasher let path = "file_path_string"
let md5 = FileHasher.md5(ofFileAtPath: path)
let sha1 = FileHasher.hash(.sha1, ofFileAtPath: path)