TeraOpenedMedia.swift (1266B)
1 import Foundation 2 import Synchronization 3 4 /// Owns the native originals until exactly one caller takes them for close. 5 /// Immutable Rust handles retain their independent files through late FFI work. 6 final class TeraOpenedMedia: Sendable { 7 let handles: [TeraPreparedMediaHandle] 8 private let files: Mutex<[FileHandle]> 9 10 init(handles: [TeraPreparedMediaHandle], files: [FileHandle]) { 11 self.handles = handles 12 self.files = Mutex(files) 13 } 14 15 deinit { 16 close() 17 } 18 19 func close() { 20 let active = files.withLock { files in 21 let active = files 22 files.removeAll() 23 return active 24 } 25 // File I/O occurs outside the short ownership-transfer critical section. 26 for file in active { 27 try? file.close() 28 } 29 } 30 } 31 32 extension TeraOpenedMedia { 33 static func open(_ values: [TeraPreparedMedia], using media: (any TeraAddMediaHandling)?) async throws -> TeraOpenedMedia { 34 guard !values.isEmpty else { return TeraOpenedMedia(handles: [], files: []) } 35 guard let media else { 36 throw TeraRuntimeFailure.local( 37 operation: "add.media.open", 38 code: "ios.add.media_unavailable", 39 safeMessage: "Prepared photos are unavailable on this device." 40 ) 41 } 42 return try await media.open(values) 43 } 44 }