diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index cd49a70..6e9e415 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -27,3 +27,9 @@ jobs: action: test warnings-as-errors: false verbosity: xcbeautify + + - name: Install SwiftLint + run: brew install swiftlint + + - name: Run SwiftLint + run: swiftlint --config .swiftlint.yml --reporter github-actions-logging diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..97111aa --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,31 @@ +included: + - AsyncImageView + - AsyncImageViewTests + - Package.swift + +excluded: + - .build + - .swiftpm + - Example/Carthage + - Example/Pods + - Docs + +opt_in_rules: + - empty_count + - explicit_init + - overridden_super_call + - redundant_nil_coalescing + +analyzer_rules: + - unused_import + - unused_optional_binding + +disabled_rules: + - force_cast + - function_body_length + - identifier_name + - line_length + - nesting + - private_over_fileprivate + - todo + - type_body_length diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a5a1ab3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +# Agent Instructions + +- Always run `swiftlint --config .swiftlint.yml` from the repository root before submitting changes. This lints the AsyncImageView codebase with the shared configuration. diff --git a/AsyncImageView/Caching.swift b/AsyncImageView/Caching.swift index ab7c6cb..4e1d926 100644 --- a/AsyncImageView/Caching.swift +++ b/AsyncImageView/Caching.swift @@ -102,9 +102,14 @@ public protocol NSDataConvertible { /// Returns the directory where all `DiskCache` caches are stored /// by default. public func diskCacheDefaultCacheDirectory() -> URL { - return try! FileManager() - .url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) - .appendingPathComponent("AsyncImageView", isDirectory: true) + do { + return try FileManager() + .url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) + .appendingPathComponent("AsyncImageView", isDirectory: true) + } catch { + assertionFailure("Failed to resolve caches directory: \(error)") + return FileManager.default.temporaryDirectory.appendingPathComponent("AsyncImageView", isDirectory: true) + } } /// `CacheType` backed by files on disk. @@ -138,9 +143,17 @@ public final class DiskCache: CacheType { self.guaranteeDirectoryExists(url.deletingLastPathComponent()) if let data = value.flatMap({ $0.data }) { - try! data.write(to: url, options: .atomicWrite) + do { + try data.write(to: url, options: .atomicWrite) + } catch { + assertionFailure("Failed to persist cache entry at \(url): \(error)") + } } else if self.fileManager.fileExists(atPath: url.path) { - try! self.fileManager.removeItem(at: url) + do { + try self.fileManager.removeItem(at: url) + } catch { + assertionFailure("Failed to delete cache entry at \(url): \(error)") + } } } } @@ -165,6 +178,10 @@ public final class DiskCache: CacheType { } private func guaranteeDirectoryExists(_ url: URL) { - try! self.fileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) + do { + try self.fileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) + } catch { + assertionFailure("Failed to create cache directory at \(url): \(error)") + } } }