Skip to content

Commit e81d0aa

Browse files
committed
refactor: use HTTPLoader for loading files for http
1 parent 77b687b commit e81d0aa

8 files changed

Lines changed: 92 additions & 66 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.margelo.nitro.rive
2+
3+
import kotlinx.coroutines.Dispatchers
4+
import kotlinx.coroutines.withContext
5+
import java.net.HttpURLConnection
6+
import java.net.URL
7+
8+
sealed class HTTPLoaderException(message: String) : Exception(message) {
9+
class InvalidURL(url: String) : HTTPLoaderException("Invalid URL: $url")
10+
class HttpError(val statusCode: Int, url: String) :
11+
HTTPLoaderException("HTTP error $statusCode for $url")
12+
}
13+
14+
object HTTPLoader {
15+
suspend fun downloadBytes(url: String): ByteArray = withContext(Dispatchers.IO) {
16+
val urlObj = URL(url)
17+
val connection = urlObj.openConnection() as HttpURLConnection
18+
19+
try {
20+
connection.requestMethod = "GET"
21+
val statusCode = connection.responseCode
22+
23+
if (statusCode !in 200..299) {
24+
throw HTTPLoaderException.HttpError(statusCode, url)
25+
}
26+
27+
connection.inputStream.use { it.readBytes() }
28+
} finally {
29+
connection.disconnect()
30+
}
31+
}
32+
}

android/src/main/java/com/margelo/nitro/rive/HybridRiveFileFactory.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import kotlinx.coroutines.Dispatchers
1212
import kotlinx.coroutines.withContext
1313
import java.io.File as JavaFile
1414
import java.net.URI
15-
import java.net.URL
1615

1716
data class FileAndCache(
1817
val file: File,
@@ -44,19 +43,16 @@ class HybridRiveFileFactory : HybridRiveFileFactorySpec() {
4443
override fun fromURL(url: String, loadCdn: Boolean, referencedAssets: ReferencedAssetsType?): Promise<HybridRiveFileSpec> {
4544
return Promise.async {
4645
try {
47-
val fileAndCache = withContext(Dispatchers.IO) {
48-
val urlObj = URL(url)
49-
val riveData = urlObj.readBytes()
50-
buildRiveFile(riveData, referencedAssets)
51-
}
46+
val riveData = HTTPLoader.downloadBytes(url)
47+
val fileAndCache = buildRiveFile(riveData, referencedAssets)
5248

5349
val hybridRiveFile = HybridRiveFile()
5450
hybridRiveFile.riveFile = fileAndCache.file
5551
hybridRiveFile.referencedAssetCache = fileAndCache.cache
5652
hybridRiveFile.assetLoader = fileAndCache.loader
5753
hybridRiveFile
5854
} catch (e: Exception) {
59-
throw Error("Failed to download Rive file: ${e.message}")
55+
throw Error("Failed to download Rive file: ${e.message}", e)
6056
}
6157
}
6258
}

android/src/main/java/com/margelo/nitro/rive/HybridRiveImageFactory.kt

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ import androidx.annotation.Keep
44
import app.rive.runtime.kotlin.core.RiveRenderImage
55
import com.facebook.proguard.annotations.DoNotStrip
66
import com.margelo.nitro.core.Promise
7-
import kotlinx.coroutines.Dispatchers
8-
import kotlinx.coroutines.withContext
9-
import java.net.HttpURLConnection
10-
import java.net.URL
117

128
@Keep
139
@DoNotStrip
@@ -16,28 +12,9 @@ class HybridRiveImageFactory : HybridRiveImageFactorySpec() {
1612
override fun loadFromURLAsync(url: String): Promise<HybridRiveImageSpec> {
1713
return Promise.async {
1814
try {
19-
val (imageData, dataSize) = withContext(Dispatchers.IO) {
20-
val urlObj = URL(url)
21-
val connection = urlObj.openConnection() as HttpURLConnection
22-
23-
try {
24-
connection.requestMethod = "GET"
25-
val statusCode = connection.responseCode
26-
27-
if (statusCode !in 200..299) {
28-
throw Exception("Failed to load image from URL: $url (status: $statusCode)")
29-
}
30-
31-
val bytes = connection.inputStream.use { it.readBytes() }
32-
Pair(bytes, bytes.size)
33-
} finally {
34-
connection.disconnect()
35-
}
36-
}
37-
15+
val imageData = HTTPLoader.downloadBytes(url)
3816
val renderImage = RiveRenderImage.fromEncoded(imageData)
39-
40-
HybridRiveImage(renderImage, dataSize)
17+
HybridRiveImage(renderImage, imageData.size)
4118
} catch (e: Exception) {
4219
throw Exception("Failed to load image from URL: $url - ${e.message}", e)
4320
}

android/src/main/java/com/margelo/nitro/rive/ReferencedAssetLoader.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class ReferencedAssetLoader {
8585
file.readBytes()
8686
}
8787
"http", "https" -> {
88-
URL(url).readBytes()
88+
HTTPLoader.downloadBytes(url)
8989
}
9090
else -> {
9191
logError("Unsupported URL scheme: ${uri.scheme}")

ios/HTTPLoader.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Foundation
2+
3+
enum HTTPLoaderError: Error, LocalizedError {
4+
case invalidURL(String)
5+
case httpError(statusCode: Int, url: URL)
6+
7+
var errorDescription: String? {
8+
switch self {
9+
case .invalidURL(let url):
10+
return "Invalid URL: \(url)"
11+
case .httpError(let code, let url):
12+
return "HTTP error \(code) for \(url)"
13+
}
14+
}
15+
}
16+
17+
final class HTTPLoader {
18+
static let shared = HTTPLoader()
19+
20+
func downloadData(from url: URL) async throws -> Data {
21+
let (data, response) = try await URLSession.shared.data(from: url)
22+
23+
if let httpResponse = response as? HTTPURLResponse,
24+
!(200...299).contains(httpResponse.statusCode)
25+
{
26+
throw HTTPLoaderError.httpError(statusCode: httpResponse.statusCode, url: url)
27+
}
28+
29+
return data
30+
}
31+
32+
func downloadData(from urlString: String) async throws -> Data {
33+
guard let url = URL(string: urlString) else {
34+
throw HTTPLoaderError.invalidURL(urlString)
35+
}
36+
return try await downloadData(from: url)
37+
}
38+
}

ios/HybridRiveFileFactory.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@ final class HybridRiveFileFactory: HybridRiveFileFactorySpec, @unchecked Sendabl
2020
/// - Throws: Runtime errors if any step fails.
2121
func genericFrom<CheckResult, Prepared>(
2222
check: @escaping () throws -> CheckResult,
23-
prepare: @escaping (CheckResult) throws -> Prepared,
23+
prepare: @escaping (CheckResult) async throws -> Prepared,
2424
fileWithCustomAssetLoader: @escaping (Prepared, @escaping LoadAsset) throws -> RiveFile,
2525
file: @escaping (Prepared) throws -> RiveFile,
2626
referencedAssets: ReferencedAssetsType?
2727
) throws -> Promise<(any HybridRiveFileSpec)> {
2828
return Promise.async {
2929
do {
3030
let checked = try check()
31+
let prepared = try await prepare(checked)
3132

3233
let result = try await withCheckedThrowingContinuation { continuation in
3334
DispatchQueue.global(qos: .userInitiated).async {
3435
do {
35-
let prepared = try prepare(checked)
3636

3737
let referencedAssetCache = SendableRef(ReferencedAssetCache())
3838
let factoryCache: SendableRef<RiveFactory?> = .init(nil)
@@ -90,7 +90,7 @@ final class HybridRiveFileFactory: HybridRiveFileFactorySpec, @unchecked Sendabl
9090
}
9191
return url
9292
},
93-
prepare: { url in try Data(contentsOf: url) },
93+
prepare: { url in try await HTTPLoader.shared.downloadData(from: url) },
9494
fileWithCustomAssetLoader: { (data, loader) in
9595
try RiveFile(data: data, loadCdn: loadCdn, customAssetLoader: loader)
9696
},

ios/HybridRiveImageFactory.swift

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,7 @@ import RiveRuntime
44
final class HybridRiveImageFactory: HybridRiveImageFactorySpec {
55
func loadFromURLAsync(url: String) throws -> Promise<(any HybridRiveImageSpec)> {
66
return Promise.async {
7-
guard let requestUrl = URL(string: url) else {
8-
throw RuntimeError.error(withMessage: "Invalid URL: \(url)")
9-
}
10-
11-
let (data, response) = try await URLSession.shared.data(from: requestUrl)
12-
13-
if let httpResponse = response as? HTTPURLResponse,
14-
!(200...299).contains(httpResponse.statusCode)
15-
{
16-
throw RuntimeError.error(
17-
withMessage: "Failed to load image from URL: \(url) (status: \(httpResponse.statusCode))")
18-
}
7+
let data = try await HTTPLoader.shared.downloadData(from: url)
198

209
guard let renderImage = RiveRenderImage(data: data) else {
2110
throw RuntimeError.error(withMessage: "Failed to decode image from URL: \(url)")

ios/ReferencedAssetLoader.swift

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ final class ReferencedAssetLoader {
4545
onError()
4646
return
4747
}
48+
4849
if let fileUrl = URL(string: url), fileUrl.scheme == "file" {
4950
do {
5051
let data = try Data(contentsOf: fileUrl)
@@ -56,26 +57,19 @@ final class ReferencedAssetLoader {
5657
return
5758
}
5859

59-
let queue = URLSession.shared
60-
guard let requestUrl = URL(string: url) else {
61-
handleInvalidUrlError(url: url)
62-
onError()
63-
return
64-
}
65-
66-
let request = URLRequest(url: requestUrl)
67-
let task = queue.dataTask(with: request) { [weak self] data, response, error in
68-
if error != nil {
69-
self?.handleInvalidUrlError(url: url)
70-
onError()
71-
} else if let data = data {
72-
listener(data)
73-
} else {
74-
onError()
60+
Task {
61+
do {
62+
let data = try await HTTPLoader.shared.downloadData(from: url)
63+
await MainActor.run {
64+
listener(data)
65+
}
66+
} catch {
67+
await MainActor.run {
68+
self.handleInvalidUrlError(url: url)
69+
onError()
70+
}
7571
}
7672
}
77-
78-
task.resume()
7973
}
8074

8175
private func processAssetBytes(

0 commit comments

Comments
 (0)