Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release ODR resource after copying the contents of the resources #590

Merged
merged 2 commits into from
Nov 21, 2023
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
1 change: 1 addition & 0 deletions Core/SystemDependencies/BundleResourceRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public protocol BundleResourceRequest: AnyObject {

func conditionallyBeginAccessingResources() async -> Bool
func beginAccessingResources() async throws
func endAccessingResources()
}

extension NSBundleResourceRequest: BundleResourceRequest {
Expand Down
6 changes: 6 additions & 0 deletions Core/SystemDependencies/FileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public protocol FileSystem: Sendable {
func removeItem(at url: URL) throws
func contentsOfDirectory(at url: URL, includingPropertiesForKeys keys: [URLResourceKey]?) throws -> [URL]
func resourceValues(at url: URL, forKeys keys: Set<URLResourceKey>) throws -> ResourceValues

func writeToFile(at path: URL, content: String) throws
}

public struct DefaultFileSystem: FileSystem {
Expand Down Expand Up @@ -47,6 +49,10 @@ public struct DefaultFileSystem: FileSystem {
public func copyItem(at srcURL: URL, to dstURL: URL) throws {
try FileManager.default.copyItem(at: srcURL, to: dstURL)
}

public func writeToFile(at path: URL, content: String) throws {
try "Success".write(to: path, atomically: true, encoding: .utf8)
}
}

public protocol ResourceValues {
Expand Down
6 changes: 6 additions & 0 deletions Core/SystemDependenciesFake/BundleResourceRequestFake.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public final class BundleResourceRequestFake: BundleResourceRequest {
public var progress = Progress()
public var loadingPriority: Double = 0
public let tags: Set<String>
public var inUse = false

public func conditionallyBeginAccessingResources() async -> Bool {
Self.resourceAvailable
Expand All @@ -32,9 +33,14 @@ public final class BundleResourceRequestFake: BundleResourceRequest {
public func beginAccessingResources() async throws {
switch Self.downloadResult {
case .success:
inUse = true
progress.completedUnitCount = progress.totalUnitCount
case .failure(let error):
throw error
}
}

public func endAccessingResources() {
inUse = false
}
}
4 changes: 4 additions & 0 deletions Core/SystemDependenciesFake/FileSystemFake.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ public final class FileSystemFake: FileSystem, Sendable {
resourceValuesByURL[url] = ResourceValuesFake(fileSize: fileSize)
}

public func writeToFile(at path: URL, content: String) throws {
files.insert(path)
}

// MARK: Private

private let state = ManagedCriticalState(State())
Expand Down
11 changes: 11 additions & 0 deletions Domain/ImageService/Sources/ImageDataService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import QuranGeometry
import QuranKit
import UIKit
import VLogging
import WordFramePersistence
import WordFrameService

Expand All @@ -32,6 +33,9 @@ public struct ImageDataService {
public func imageForPage(_ page: Page) async throws -> ImagePage {
let imageURL = imageURLForPage(page)
guard let image = UIImage(contentsOfFile: imageURL.path) else {
logFiles(directory: imagesURL) // <reading>/images/width/
logFiles(directory: imagesURL.deletingLastPathComponent()) // <reading>/images/
logFiles(directory: imagesURL.deletingLastPathComponent().deletingLastPathComponent()) // <reading>/
fatalError("No image found for page '\(page)'")
}

Expand All @@ -51,6 +55,13 @@ public struct ImageDataService {
private let cropInsets: UIEdgeInsets
private let imagesURL: URL

private func logFiles(directory: URL) {
let fileManager = FileManager.default
let files = (try? fileManager.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)) ?? []
let fileNames = files.map(\.lastPathComponent)
logger.error("Images: Directory \(directory) contains files \(fileNames)")
}

private func imageURLForPage(_ page: Page) -> URL {
imagesURL.appendingPathComponent("page\(page.pageNumber.as3DigitString()).png")
}
Expand Down
12 changes: 8 additions & 4 deletions Domain/ReadingService/Sources/OnDemandResource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ struct OnDemandResource {
// MARK: Internal

func fetch(onProgressChange: @Sendable @escaping (Double) -> Void) async throws {
logger.info("Fetching resources \(request.tags)")
logger.info("Resources: Fetch resources \(request.tags)")
let available = await request.conditionallyBeginAccessingResources()
logger.info("Resources \(request.tags) availability \(available)")
logger.info("Resources: \(request.tags) has been downloaded before? \(available)")
if available {
return
}
Expand All @@ -35,13 +35,17 @@ struct OnDemandResource {
}
do {
try await request.beginAccessingResources()
logger.info("Resources \(request.tags) downloaded")
logger.info("Resources: \(request.tags) has been downloaded")
} catch {
logger.error("Resources \(request.tags) failed. Error: \(error)")
logger.error("Resources: \(request.tags) has failed to download. Error: \(error)")
throw error
}
}

func endAccessingResources() {
request.endAccessingResources()
}

// MARK: Private

private let request: BundleResourceRequest
Expand Down
35 changes: 19 additions & 16 deletions Domain/ReadingService/Sources/ReadingResourcesService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,38 +88,35 @@ public actor ReadingResourcesService {
}

private func loadResource(of reading: Reading) async {
if fileManager.fileExists(at: reading.directory) {
logger.info("Resources: Start loading reading resources of: \(reading)")
if fileManager.fileExists(at: reading.successFilePath) {
logger.info("Resources: Reading \(reading) has been downloaded and saved locally before")
send(.ready, from: reading)
return
}

let tag = reading.resourcesTag
let resource = OnDemandResource(request: resourceRequestFactory([tag]))
defer { resource.endAccessingResources() }
do {
try await resource.fetch(onProgressChange: { progress in
self.send(.downloading(progress: progress), from: reading)
})

copyFiles(reading: reading)
try copyFiles(reading: reading)
send(.ready, from: reading)
} catch {
send(.error(error as NSError), from: reading)
}
}

private func copyFiles(reading: Reading) {
if preferences.reading != reading {
return
}
if reading.directory.isReachable {
return
}

private func copyFiles(reading: Reading) throws {
// Create `resources` directory if needed.
try? fileManager.createDirectory(at: Reading.resourcesDirectory, withIntermediateDirectories: true)

// Remove previously downloaded resources.
removePreviouslyDownloadedResources()

copyNewlyDownloadedResource(reading: reading)
// Copy new reading to `resources` directory.
try copyNewlyDownloadedResource(reading: reading)
}

private func removePreviouslyDownloadedResources() {
Expand All @@ -129,16 +126,18 @@ public actor ReadingResourcesService {
try? fileManager.removeItem(at: resource)
}
} catch {
logger.error("Resources failed to list files. Error: \(error)")
logger.error("Resources: Failed to list files in resources directory. Error: \(error)")
}
}

private func copyNewlyDownloadedResource(reading: Reading) {
private func copyNewlyDownloadedResource(reading: Reading) throws {
do {
let bundleURL = reading.url(inBundle: bundle)
try fileManager.copyItem(at: bundleURL, to: reading.directory)
try fileManager.writeToFile(at: reading.successFilePath, content: "Downloaded")
} catch {
logger.error("Resources \(reading.resourcesTag) failed to copy. Error: \(error)")
logger.error("Resources: \(reading) failed to copy. Error: \(error)")
throw error
}
}

Expand Down Expand Up @@ -171,4 +170,8 @@ extension Reading {
public var directory: URL {
Self.resourcesDirectory.appendingPathComponent(resourcesTag, isDirectory: true)
}

var successFilePath: URL {
directory.appendingPathComponent("success-v2.txt")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class ReadingResourcesServiceTests: XCTestCase {
}

func test_resourceNotAvailable_downloaded() async throws {
fileManager.files.insert(Reading.hafs_1405.directory)
fileManager.files.insert(Reading.hafs_1405.successFilePath)
BundleResourceRequestFake.resourceAvailable = false
await service.startLoadingResources()

Expand Down
Loading