Skip to content

Commit 8de7f49

Browse files
Sendable Hub.Downloader, Hub.Hub and Hub.HubApi .2
1 parent 49a760d commit 8de7f49

File tree

6 files changed

+257
-215
lines changed

6 files changed

+257
-215
lines changed

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
import PackageDescription
55

6-
// Define the strict concurrency settings to be applied to all targets.
6+
/// Define the strict concurrency settings to be applied to all targets.
77
let swiftSettings: [SwiftSetting] = [
8-
.enableExperimentalFeature("StrictConcurrency")
8+
.enableExperimentalFeature("StrictConcurrency"),
99
]
1010

1111
let package = Package(

Sources/Hub/Downloader.swift

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ final class Downloader: NSObject, Sendable, ObservableObject {
2929
}
3030

3131
private let broadcaster: Broadcaster<DownloadState> = Broadcaster<DownloadState> {
32-
return DownloadState.notStarted
32+
DownloadState.notStarted
3333
}
3434

3535
private let sessionConfig: URLSessionConfiguration
36-
let session: SessionActor = SessionActor()
37-
private let task: TaskActor = TaskActor()
36+
let session: SessionActor = .init()
37+
private let task: TaskActor = .init()
3838

3939
init(
4040
to destination: URL,
4141
incompleteDestination: URL,
4242
inBackground: Bool = false,
43-
chunkSize: Int = 10 * 1024 * 1024 // 10MB
43+
chunkSize: Int = 10 * 1024 * 1024 // 10MB
4444
) {
4545
self.destination = destination
4646
// Create incomplete file path based on destination
@@ -55,7 +55,7 @@ final class Downloader: NSObject, Sendable, ObservableObject {
5555
config.isDiscretionary = false
5656
config.sessionSendsLaunchEvents = true
5757
}
58-
self.sessionConfig = config
58+
sessionConfig = config
5959
}
6060

6161
func download(
@@ -66,13 +66,13 @@ final class Downloader: NSObject, Sendable, ObservableObject {
6666
timeout: TimeInterval = 10,
6767
numRetries: Int = 5
6868
) async -> AsyncStream<DownloadState> {
69-
if let task = await self.task.get() {
69+
if let task = await task.get() {
7070
task.cancel()
7171
}
72-
await self.downloadResumeState.setExpectedSize(expectedSize)
73-
let resumeSize = Self.incompleteFileSize(at: self.incompleteDestination)
74-
await self.session.set(URLSession(configuration: self.sessionConfig, delegate: self, delegateQueue: nil))
75-
await self.setUpDownload(
72+
await downloadResumeState.setExpectedSize(expectedSize)
73+
let resumeSize = Self.incompleteFileSize(at: incompleteDestination)
74+
await session.set(URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil))
75+
await setUpDownload(
7676
from: url,
7777
with: authToken,
7878
resumeSize: resumeSize,
@@ -81,7 +81,7 @@ final class Downloader: NSObject, Sendable, ObservableObject {
8181
numRetries: numRetries
8282
)
8383

84-
return await self.broadcaster.subscribe()
84+
return await broadcaster.subscribe()
8585
}
8686

8787
/// Sets up and initiates a file download operation
@@ -102,8 +102,8 @@ final class Downloader: NSObject, Sendable, ObservableObject {
102102
timeout: TimeInterval,
103103
numRetries: Int
104104
) async {
105-
let resumeSize = Self.incompleteFileSize(at: self.incompleteDestination)
106-
guard let tasks = await self.session.get()?.allTasks else {
105+
let resumeSize = Self.incompleteFileSize(at: incompleteDestination)
106+
guard let tasks = await session.get()?.allTasks else {
107107
return
108108
}
109109

@@ -123,7 +123,7 @@ final class Downloader: NSObject, Sendable, ObservableObject {
123123
}
124124
}
125125

126-
await self.task.set(
126+
await task.set(
127127
Task {
128128
do {
129129
var request = URLRequest(url: url)
@@ -132,7 +132,7 @@ final class Downloader: NSObject, Sendable, ObservableObject {
132132
var requestHeaders = headers ?? [:]
133133

134134
// Populate header auth and range fields
135-
if let authToken = authToken {
135+
if let authToken {
136136
requestHeaders["Authorization"] = "Bearer \(authToken)"
137137
}
138138

@@ -203,14 +203,14 @@ final class Downloader: NSObject, Sendable, ObservableObject {
203203
tempFile: FileHandle,
204204
numRetries: Int
205205
) async throws {
206-
guard let session = await self.session.get() else {
206+
guard let session = await session.get() else {
207207
throw DownloadError.unexpectedError
208208
}
209209

210210
// Create a new request with Range header for resuming
211211
var newRequest = request
212-
if await self.downloadResumeState.downloadedSize > 0 {
213-
newRequest.setValue("bytes=\(await self.downloadResumeState.downloadedSize)-", forHTTPHeaderField: "Range")
212+
if await downloadResumeState.downloadedSize > 0 {
213+
await newRequest.setValue("bytes=\(downloadResumeState.downloadedSize)-", forHTTPHeaderField: "Range")
214214
}
215215

216216
// Start the download and get the byte stream
@@ -233,22 +233,22 @@ final class Downloader: NSObject, Sendable, ObservableObject {
233233
buffer.append(byte)
234234
// When buffer is full, write to disk
235235
if buffer.count == chunkSize {
236-
if !buffer.isEmpty { // Filter out keep-alive chunks
236+
if !buffer.isEmpty { // Filter out keep-alive chunks
237237
try tempFile.write(contentsOf: buffer)
238238
buffer.removeAll(keepingCapacity: true)
239239

240-
await self.downloadResumeState.incDownloadedSize(chunkSize)
240+
await downloadResumeState.incDownloadedSize(chunkSize)
241241
newNumRetries = 5
242-
guard let expectedSize = await self.downloadResumeState.expectedSize else { continue }
243-
let progress = expectedSize != 0 ? Double(await self.downloadResumeState.downloadedSize) / Double(expectedSize) : 0
244-
await self.broadcaster.broadcast(state: .downloading(progress))
242+
guard let expectedSize = await downloadResumeState.expectedSize else { continue }
243+
let progress = await expectedSize != 0 ? Double(downloadResumeState.downloadedSize) / Double(expectedSize) : 0
244+
await broadcaster.broadcast(state: .downloading(progress))
245245
}
246246
}
247247
}
248248

249249
if !buffer.isEmpty {
250250
try tempFile.write(contentsOf: buffer)
251-
await self.downloadResumeState.incDownloadedSize(buffer.count)
251+
await downloadResumeState.incDownloadedSize(buffer.count)
252252
buffer.removeAll(keepingCapacity: true)
253253
newNumRetries = 5
254254
}
@@ -270,15 +270,15 @@ final class Downloader: NSObject, Sendable, ObservableObject {
270270

271271
// Verify the downloaded file size matches the expected size
272272
let actualSize = try tempFile.seekToEnd()
273-
if let expectedSize = await self.downloadResumeState.expectedSize, expectedSize != actualSize {
273+
if let expectedSize = await downloadResumeState.expectedSize, expectedSize != actualSize {
274274
throw DownloadError.unexpectedError
275275
}
276276
}
277277

278278
func cancel() async {
279-
await self.session.get()?.invalidateAndCancel()
280-
await self.task.get()?.cancel()
281-
await self.broadcaster.broadcast(state: .failed(URLError(.cancelled)))
279+
await session.get()?.invalidateAndCancel()
280+
await task.get()?.cancel()
281+
await broadcaster.broadcast(state: .failed(URLError(.cancelled)))
282282
}
283283

284284
/// Check if an incomplete file exists for the destination and returns its size
@@ -305,7 +305,7 @@ extension Downloader: URLSessionDownloadDelegate {
305305
func urlSession(_: URLSession, downloadTask _: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
306306
do {
307307
// If the downloaded file already exists on the filesystem, overwrite it
308-
try FileManager.default.moveDownloadedFile(from: location, to: self.destination)
308+
try FileManager.default.moveDownloadedFile(from: location, to: destination)
309309
Task {
310310
await self.broadcaster.broadcast(state: .completed(destination))
311311
}
@@ -317,7 +317,7 @@ extension Downloader: URLSessionDownloadDelegate {
317317
}
318318

319319
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
320-
if let error = error {
320+
if let error {
321321
Task {
322322
await self.broadcaster.broadcast(state: .failed(error))
323323
}
@@ -347,15 +347,15 @@ private actor DownloadResumeState {
347347
var downloadedSize: Int = 0
348348

349349
func setExpectedSize(_ size: Int?) {
350-
self.expectedSize = size
350+
expectedSize = size
351351
}
352352

353353
func setDownloadedSize(_ size: Int) {
354-
self.downloadedSize = size
354+
downloadedSize = size
355355
}
356356

357357
func incDownloadedSize(_ size: Int) {
358-
self.downloadedSize += size
358+
downloadedSize += size
359359
}
360360
}
361361

@@ -373,7 +373,7 @@ actor Broadcaster<E: Sendable> {
373373
}
374374

375375
func subscribe() -> AsyncStream<E> {
376-
return AsyncStream { continuation in
376+
AsyncStream { continuation in
377377
let id = UUID()
378378
self.continuations[id] = continuation
379379

@@ -396,11 +396,11 @@ actor Broadcaster<E: Sendable> {
396396
}
397397

398398
private func unsubscribe(_ id: UUID) {
399-
self.continuations.removeValue(forKey: id)
399+
continuations.removeValue(forKey: id)
400400
}
401401

402402
func broadcast(state: E) async {
403-
self.latestState = state
403+
latestState = state
404404
await withTaskGroup(of: Void.self) { group in
405405
for continuation in continuations.values {
406406
group.addTask {
@@ -412,25 +412,25 @@ actor Broadcaster<E: Sendable> {
412412
}
413413

414414
actor SessionActor {
415-
private var urlSession: URLSession? = nil
415+
private var urlSession: URLSession?
416416

417417
func set(_ urlSession: URLSession?) {
418418
self.urlSession = urlSession
419419
}
420420

421421
func get() -> URLSession? {
422-
return self.urlSession
422+
urlSession
423423
}
424424
}
425425

426426
actor TaskActor {
427-
private var task: Task<Void, Error>? = nil
427+
private var task: Task<Void, Error>?
428428

429429
func set(_ task: Task<Void, Error>?) {
430430
self.task = task
431431
}
432432

433433
func get() -> Task<Void, Error>? {
434-
return self.task
434+
task
435435
}
436436
}

Sources/Hub/Hub.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
import Foundation
99

10-
public struct Hub: Sendable {}
10+
public struct Hub: Sendable { }
1111

12-
extension Hub {
13-
public enum HubClientError: LocalizedError {
12+
public extension Hub {
13+
enum HubClientError: LocalizedError {
1414
case authorizationRequired
1515
case httpStatusCode(Int)
1616
case parse
@@ -51,13 +51,13 @@ extension Hub {
5151
}
5252
}
5353

54-
public enum RepoType: String, Codable {
54+
enum RepoType: String, Codable {
5555
case models
5656
case datasets
5757
case spaces
5858
}
5959

60-
public struct Repo: Codable {
60+
struct Repo: Codable {
6161
public let id: String
6262
public let type: RepoType
6363

@@ -82,17 +82,17 @@ public final class LanguageModelConfigurationFromHub: Sendable {
8282
revision: String = "main",
8383
hubApi: HubApi = .shared
8484
) {
85-
self.configPromise = Task.init {
86-
return try await Self.loadConfig(modelName: modelName, revision: revision, hubApi: hubApi)
85+
configPromise = Task.init {
86+
try await Self.loadConfig(modelName: modelName, revision: revision, hubApi: hubApi)
8787
}
8888
}
8989

9090
public init(
9191
modelFolder: URL,
9292
hubApi: HubApi = .shared
9393
) {
94-
self.configPromise = Task {
95-
return try await Self.loadConfig(modelFolder: modelFolder, hubApi: hubApi)
94+
configPromise = Task {
95+
try await Self.loadConfig(modelFolder: modelFolder, hubApi: hubApi)
9696
}
9797
}
9898

@@ -204,7 +204,7 @@ public final class LanguageModelConfigurationFromHub: Sendable {
204204
// Try to load .jinja template as plain text
205205
chatTemplate = try? String(contentsOf: chatTemplateJinjaURL, encoding: .utf8)
206206
} else if FileManager.default.fileExists(atPath: chatTemplateJsonURL.path),
207-
let chatTemplateConfig = try? hubApi.configuration(fileURL: chatTemplateJsonURL)
207+
let chatTemplateConfig = try? hubApi.configuration(fileURL: chatTemplateJsonURL)
208208
{
209209
// Fall back to .json template
210210
chatTemplate = chatTemplateConfig.chatTemplate.string()

0 commit comments

Comments
 (0)