Files
edgeever/apps/ios/EdgeEver/Data/Cache/ResourceCache.swift
T
tianma-if cd4f010d8f feat(ios): add native SwiftUI client with Android UI parity
Replace the abandoned RN iOS path with a SwiftUI app (iOS 17+) that reuses
the shared TipTap editor bundle, GRDB local mirror/outbox, and Android-aligned
shell chrome for list, create, detail, and Me. Package the editor HTML in the
app resources so Markdown renders, present edit from the workspace root with a
UIKit FAB, and keep caret stable while typing.
2026-08-07 02:52:01 +08:00

74 lines
2.8 KiB
Swift

import Foundation
import CryptoKit
actor ResourceCache {
private let directory: URL
private let maxBytes: Int64 = 200 * 1024 * 1024
init() {
let base = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
directory = base.appendingPathComponent("resource-cache", isDirectory: true)
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
}
func fileURL(for resourceId: String) -> URL {
let safe = resourceId.replacingOccurrences(of: "/", with: "_")
return directory.appendingPathComponent(safe)
}
func dataURL(for resourceId: String, data: Data, mimeType: String) throws -> String {
let url = fileURL(for: resourceId)
try data.write(to: url, options: .atomic)
let base64 = data.base64EncodedString()
return "data:\(mimeType);base64,\(base64)"
}
func cachedData(for resourceId: String) -> Data? {
let url = fileURL(for: resourceId)
return try? Data(contentsOf: url)
}
static func resourceId(from source: String) -> String? {
// /api/v1/resources/:id or .../blob
guard let url = URL(string: source, relativeTo: URL(string: "https://placeholder.local")) else { return nil }
let parts = url.path.split(separator: "/").map(String.init)
if let idx = parts.firstIndex(of: "resources"), idx + 1 < parts.count {
return parts[idx + 1]
}
return nil
}
/// Match Android `normalizeProtectedResourcePath` — ensure blob route.
static func normalizeProtectedResourcePath(_ source: String, baseURL: URL?) -> String {
var path = source
if let base = baseURL?.absoluteString.trimmingCharacters(in: CharacterSet(charactersIn: "/")),
path.hasPrefix(base + "/")
{
path = String(path.dropFirst(base.count))
}
if path.hasPrefix("http://") || path.hasPrefix("https://"),
let url = URL(string: path)
{
path = url.path
}
guard path.hasPrefix("/api/v1/resources/") else { return path }
if path.range(of: #"/blob(?:$|[?#])"#, options: .regularExpression) != nil {
return path
}
if let match = path.range(of: #"^/api/v1/resources/[^/?#]+"#, options: .regularExpression) {
return String(path[match]) + "/blob"
}
return path
}
static func isProtectedResourceSource(_ source: String, baseURL: URL?) -> Bool {
if source.hasPrefix("/api/v1/resources/") { return true }
if let base = baseURL?.absoluteString.trimmingCharacters(in: CharacterSet(charactersIn: "/")),
source.hasPrefix(base + "/api/v1/resources/")
{
return true
}
return false
}
}