-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathHybridRiveFileFactory.swift
More file actions
163 lines (151 loc) · 6.23 KB
/
HybridRiveFileFactory.swift
File metadata and controls
163 lines (151 loc) · 6.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import NitroModules
import RiveRuntime
final class HybridRiveFileFactory: HybridRiveFileFactorySpec, @unchecked Sendable {
let assetLoader = ReferencedAssetLoader()
/// Asynchronously creates a `HybridRiveFileSpec` by performing the following steps:
/// 1. Executes `check()` to validate or fetch initial data.
/// 2. Processes the result with `prepare()`.
/// 3. If a custom asset loader is available, loads the file using `fileWithCustomAssetLoader(prepared, assetLoader)`.
/// Otherwise, loads the file using `file(prepared)`.
/// 4. Handles referenced assets and caches as needed.
/// - Parameters:
/// - check: Closure to validate or fetch initial data.
/// - prepare: Closure to process the checked result.
/// - fileWithCustomAssetLoader: Closure to load the file with a custom asset loader.
/// - file: Closure to load the file without a custom asset loader.
/// - referencedAssets: Optional referenced assets.
/// - Returns: A promise resolving to a `HybridRiveFileSpec`.
/// - Throws: Runtime errors if any step fails.
func genericFrom<CheckResult, Prepared>(
check: @escaping () throws -> CheckResult,
prepare: @escaping (CheckResult) throws -> Prepared,
fileWithCustomAssetLoader: @escaping (Prepared, @escaping LoadAsset) throws -> RiveFile,
file: @escaping (Prepared) throws -> RiveFile,
referencedAssets: ReferencedAssetsType?
) throws -> Promise<(any HybridRiveFileSpec)> {
return Promise.async {
do {
let checked = try check()
let result = try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
do {
let prepared = try prepare(checked)
let referencedAssetCache = SendableRef(ReferencedAssetCache())
let factoryCache: SendableRef<RiveFactory?> = .init(nil)
let customLoader = self.assetLoader.createCustomLoader(
referencedAssets: referencedAssets, cache: referencedAssetCache,
factory: factoryCache)
let riveFile =
if let customLoader = customLoader {
try fileWithCustomAssetLoader(prepared, customLoader)
} else {
try file(prepared)
}
let result = (
file: riveFile, cache: referencedAssetCache.value, factory: factoryCache.value,
loader: customLoader != nil ? self.assetLoader : nil
)
DispatchQueue.main.async {
continuation.resume(returning: result)
}
} catch {
DispatchQueue.main.async {
continuation.resume(throwing: error)
}
}
}
}
let hybridRiveFile = HybridRiveFile()
hybridRiveFile.riveFile = result.file
hybridRiveFile.referencedAssetCache = result.cache
if let factory = result.factory {
hybridRiveFile.cachedFactory = factory
}
hybridRiveFile.assetLoader = result.loader
return hybridRiveFile
} catch let error as NSError {
throw RuntimeError.error(
withMessage: "Failed to download Rive file: \(error.localizedDescription)")
} catch {
throw RuntimeError.error(withMessage: "Unknown error occurred while downloading Rive file")
}
}
}
// MARK: Public Methods
func fromURL(url: String, loadCdn: Bool, referencedAssets: ReferencedAssetsType?) throws
-> Promise<(any HybridRiveFileSpec)>
{
return try genericFrom(
check: {
guard let url = URL(string: url) else {
throw RuntimeError.error(withMessage: "Invalid URL: \(url)")
}
return url
},
prepare: { url in try Data(contentsOf: url) },
fileWithCustomAssetLoader: { (data, loader) in
try RiveFile(data: data, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (data) in try RiveFile(data: data, loadCdn: loadCdn) },
referencedAssets: referencedAssets
)
}
func fromFileURL(fileURL: String, loadCdn: Bool, referencedAssets: ReferencedAssetsType?) throws
-> Promise<(any HybridRiveFileSpec)>
{
return try genericFrom(
check: {
guard let url = URL(string: fileURL) else {
throw RuntimeError.error(withMessage: "Invalid URL: \(fileURL)")
}
guard url.isFileURL else {
throw RuntimeError.error(withMessage: "fromFileURL: URL must be a file URL: \(fileURL)")
}
return url
},
prepare: { url in try Data(contentsOf: url) },
fileWithCustomAssetLoader: { (data, loader) in
try RiveFile(data: data, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (data) in try RiveFile(data: data, loadCdn: loadCdn) },
referencedAssets: referencedAssets
)
}
func fromResource(resource: String, loadCdn: Bool, referencedAssets: ReferencedAssetsType?) throws
-> Promise<(any HybridRiveFileSpec)>
{
return try genericFrom(
check: {
guard Bundle.main.path(forResource: resource, ofType: "riv") != nil else {
throw RuntimeError.error(withMessage: "Could not find Rive file: \(resource).riv")
}
return resource
},
prepare: { $0 },
fileWithCustomAssetLoader: { (resource, loader) in
try RiveFile(resource: resource, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (resource) in try RiveFile(resource: resource, loadCdn: loadCdn) },
referencedAssets: referencedAssets
)
}
func fromResource(resource: String, loadCdn: Bool) throws -> Promise<(any HybridRiveFileSpec)> {
return try fromResource(resource: resource, loadCdn: loadCdn, referencedAssets: nil)
}
func fromBytes(bytes: ArrayBufferHolder, loadCdn: Bool, referencedAssets: ReferencedAssetsType?)
throws -> Promise<
(any HybridRiveFileSpec)
>
{
let data = bytes.toData(copyIfNeeded: false)
return try genericFrom(
check: { data },
prepare: { $0 },
fileWithCustomAssetLoader: { (data, loader) in
try RiveFile(data: data, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (data) in try RiveFile(data: data, loadCdn: loadCdn) },
referencedAssets: referencedAssets
)
}
}