diff --git a/Source/Mp3/Mp3FileReader.swift b/Source/Mp3/Mp3FileReader.swift index 126dfff6..db33fd5a 100644 --- a/Source/Mp3/Mp3FileReader.swift +++ b/Source/Mp3/Mp3FileReader.swift @@ -60,7 +60,7 @@ class Mp3FileReader { let readHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path)) defer { - if #available(iOS 13.0, *) { + if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { try? readHandle.close() } else { readHandle.closeFile() @@ -84,7 +84,7 @@ class Mp3FileReader { private func read(bytesCount: Int, from fileHandle: FileHandle) throws -> Data { let result = try { - if #available(iOS 13.4, *) { + if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { return try fileHandle.read(upToCount: bytesCount) } else { return fileHandle.readData(ofLength: bytesCount) diff --git a/Source/Mp3/Mp3FileWriter.swift b/Source/Mp3/Mp3FileWriter.swift index acf08b3b..474b6788 100644 --- a/Source/Mp3/Mp3FileWriter.swift +++ b/Source/Mp3/Mp3FileWriter.swift @@ -19,23 +19,23 @@ class Mp3FileWriter { if toPath != fromPath { return toPath } - + return FileManager.default.temporaryDirectory.appendingPathComponent("\(UUID().uuidString).mp3").path }() - + defer { if temporaryPath != toPath { try? FileManager.default.removeItem(atPath: temporaryPath) } } - + try eventuallyCreateIntermediatesDirectoriesFor(path: temporaryPath) try newId3TagData.write(to: URL(fileURLWithPath: temporaryPath)) - + // Create file handles let readHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: fromPath)) defer { - if #available(iOS 13.0, *) { + if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { try? readHandle.close() } else { readHandle.closeFile() @@ -44,7 +44,7 @@ class Mp3FileWriter { let writeHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: temporaryPath)) defer { - if #available(iOS 13.0, *) { + if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { try? writeHandle.close() } else { writeHandle.closeFile() @@ -52,25 +52,25 @@ class Mp3FileWriter { } // Seek over the tag of the existing file, then copy the rest in chunks - if #available(iOS 13.4, *) { + if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { try writeHandle.seekToEnd() } else { writeHandle.seekToEndOfFile() } if let currentId3TagData = currentId3TagData { - if #available(iOS 13.0, *) { + if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { try readHandle.seek(toOffset: UInt64(currentId3TagData.count)) } else { readHandle.seek(toFileOffset: UInt64(currentId3TagData.count)) } } - + var isFinished = false while !isFinished { - try autoreleasepool { + let work = { let chunk = try { - if #available(iOS 13.4, *) { + if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { return try readHandle.read(upToCount: 131072) // 128 KB } else { return readHandle.readData(ofLength: 131072) // 128 KB @@ -78,7 +78,7 @@ class Mp3FileWriter { }() if let chunk, !chunk.isEmpty { - if #available(iOS 13.4, *) { + if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { try writeHandle.write(contentsOf: chunk) } else { writeHandle.write(chunk) @@ -87,11 +87,35 @@ class Mp3FileWriter { isFinished = true } } + +#if canImport(ObjectiveC) + // autoreleasepool is only needed in Objective-C environment (not on Linux) + try autoreleasepool(invoking: work) +#else + try work() +#endif } - + // Replace the file if temporaryPath != toPath { +#if os(Linux) + // For some reason the FileManager.replaceItemAt(_:withItemAt:) doesn't work on Linux and fails with `NSFileWriteUnknownError` + let backupPath = URL(fileURLWithPath: toPath).appendingPathExtension("tmp").path + try FileManager.default.copyItem(atPath: toPath, toPath: backupPath) + defer { + try? FileManager.default.removeItem(atPath: backupPath) + } + + do { + try FileManager.default.removeItem(atPath: toPath) + try FileManager.default.copyItem(atPath: temporaryPath, toPath: toPath) + } catch { + try? FileManager.default.copyItem(atPath: backupPath, toPath: toPath) + throw error + } +#else _ = try FileManager.default.replaceItemAt(validPath, withItemAt: URL(fileURLWithPath: temporaryPath)) +#endif } }