Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DevLog/App/Assembler/DataAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ final class DataAssembler: Assembler {
)
}

container.register(WebPageImageRepository.self) {
WebPageImageRepositoryImpl(
store: container.resolve(WebPageImageStore.self)
)
}

container.register(UserPreferencesRepository.self) {
UserPreferencesRepositoryImpl(
store: container.resolve(UserDefaultsStore.self),
Expand Down
8 changes: 8 additions & 0 deletions DevLog/App/Assembler/DomainAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,18 @@ private extension DomainAssembler {
FetchWebPagesUseCaseImpl(container.resolve(WebPageRepository.self))
}

container.register(FetchWebPageImageDirSizeUseCase.self) {
FetchWebPageImageDirSizeUseCaseImpl(container.resolve(WebPageImageRepository.self))
}

container.register(AddWebPageUseCase.self) {
AddWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
}

container.register(ClearWebPageImageDirectoryUseCase.self) {
ClearWebPageImageDirectoryUseCaseImpl(container.resolve(WebPageImageRepository.self))
}

container.register(DeleteWebPageUseCase.self) {
DeleteWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
}
Expand Down
4 changes: 3 additions & 1 deletion DevLog/App/Assembler/InfraAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ final class InfraAssembler: Assembler {
}

container.register(WebPageMetadataService.self) {
WebPageMetadataService()
WebPageMetadataService(
store: container.resolve(WebPageImageStore.self)
)
}

container.register(NWPathConnectivityProvider.self) {
Expand Down
4 changes: 4 additions & 0 deletions DevLog/App/Assembler/PersistenceAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ final class PersistenceAssembler: Assembler {
container.register(ThemeStore.self) {
ThemeStore()
}

container.register(WebPageImageStore.self) {
WebPageImageStore()
}
}
}
28 changes: 28 additions & 0 deletions DevLog/Data/Repository/WebPageImageRepositoryImpl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// WebPageImageRepositoryImpl.swift
// DevLog
//
// Created by opfic on 4/14/26.
//

final class WebPageImageRepositoryImpl: WebPageImageRepository {
private let store: WebPageImageStore

init(store: WebPageImageStore) {
self.store = store
}

func fetchDirSizeInBytes() async -> Int64 {
let store = self.store
return await Task.detached(priority: .utility) {
store.dirSizeInBytes()
}.value
}

func clearDirectory() async throws {
let store = self.store
try await Task.detached(priority: .utility) {
try store.clearDirectory()
}.value
}
}
11 changes: 11 additions & 0 deletions DevLog/Domain/Protocol/WebPageImageRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// WebPageImageRepository.swift
// DevLog
//
// Created by opfic on 4/14/26.
//

protocol WebPageImageRepository {
func fetchDirSizeInBytes() async -> Int64
func clearDirectory() async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// FetchWebPageImageDirSizeUseCase.swift
// DevLog
//
// Created by opfic on 4/14/26.
//

protocol FetchWebPageImageDirSizeUseCase {
func execute() async -> Int64
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// FetchWebPageImageDirSizeUseCaseImpl.swift
// DevLog
//
// Created by opfic on 4/14/26.
//

final class FetchWebPageImageDirSizeUseCaseImpl: FetchWebPageImageDirSizeUseCase {
private let repository: WebPageImageRepository

init(_ repository: WebPageImageRepository) {
self.repository = repository
}

func execute() async -> Int64 {
await repository.fetchDirSizeInBytes()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// ClearWebPageImageDirectoryUseCase.swift
// DevLog
//
// Created by opfic on 4/14/26.
//

protocol ClearWebPageImageDirectoryUseCase {
func execute() async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// ClearWebPageImageDirectoryUseCaseImpl.swift
// DevLog
//
// Created by opfic on 4/14/26.
//

final class ClearWebPageImageDirectoryUseCaseImpl: ClearWebPageImageDirectoryUseCase {
private let repository: WebPageImageRepository

init(_ repository: WebPageImageRepository) {
self.repository = repository
}

func execute() async throws {
try await repository.clearDirectory()
}
}
52 changes: 10 additions & 42 deletions DevLog/Infra/Service/WebPageMetadataService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ import LinkPresentation
import UIKit

final class WebPageMetadataService {
private let imageStore: WebPageImageStore
private let logger = Logger(category: "WebPageMetadataService")

init(store: WebPageImageStore) {
self.imageStore = store
}

func fetchMetadata(from urlString: String) async throws -> WebPageMetadataResponse {
logger.info("Fetching metadata for URL: \(urlString)")

Expand Down Expand Up @@ -46,12 +51,7 @@ final class WebPageMetadataService {
}

do {
let removed = try await Task.detached(priority: .utility) {
let fileURL = try Self.cacheFileURL(for: url)
guard FileManager.default.fileExists(atPath: fileURL.path) else { return false }
try FileManager.default.removeItem(at: fileURL)
return true
}.value
let removed = try imageStore.removeImage(for: url)

if removed {
logger.info("Removed cached image for URL: \(urlString)")
Expand All @@ -66,11 +66,12 @@ final class WebPageMetadataService {
throw URLError(.badURL)
}

return try Self.cacheFileURL(for: url)
return try imageStore.cachedImageURL(for: url)
}

private func extractImageURL(from imageProvider: NSItemProvider?, url: URL) async throws -> URL? {
guard let imageProvider else { return nil }
let imageStore = self.imageStore

return try await withCheckedThrowingContinuation { continuation in
imageProvider.loadObject(ofClass: UIImage.self) { image, error in
Expand All @@ -86,45 +87,12 @@ final class WebPageMetadataService {
}

do {
let fileURL = try Self.cacheFileURL(for: url)
Task.detached { [data, fileURL] in
do {
try data.write(to: fileURL, options: [.atomic])
continuation.resume(returning: fileURL)
} catch {
continuation.resume(throwing: error)
}
}
let fileURL = try imageStore.saveImage(data, for: url)
continuation.resume(returning: fileURL)
} catch {
continuation.resume(throwing: error)
}
}
}
}

private static func cacheFileURL(for url: URL) throws -> URL {
let imageDir = try imageDirectoryURL()

let fileName = url.absoluteString
.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? UUID().uuidString

return imageDir
.appendingPathComponent(fileName)
.appendingPathExtension("jpeg")
}

private static func imageDirectoryURL() throws -> URL {
let cachesDir = try FileManager.default.url(
for: .cachesDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
let imageDir = cachesDir.appendingPathComponent("webPageImages", isDirectory: true)
if !FileManager.default.fileExists(atPath: imageDir.path) {
try FileManager.default.createDirectory(at: imageDir, withIntermediateDirectories: true)
}

return imageDir
}
}
Loading