Mac -> Linux: Everything you can do... I can do somewhat
Got the tipsy-robot-swift dev branch to run on Linux with some changes to APITizer and TrunkLink dev branches as well, much of the progress is on the APIng “for_linux_test” branch.
All of this will need to be revisited after the release of the new Swift Foundation, presumably after WWDC 2023 in June. Many of these patches will probably be deleted.
Still TODO
ServerSideEvents engine not implemented on Linux b/c currently uses the URLSession.bytes(for)
async convenience function and I don’t need it for phase 1 of my posts-only bot, and I’m just going to wait until after WWDC. References to it wrapped in #if !os(Linux)
checks.
Misc Headers
File headers where necessary.
Networking
Used by Linux to load URLRequest, etc.
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
UTTypes
Used by AppleOS b/c they have it.
#if canImport(UniformTypeIdentifiers)
import UniformTypeIdentifiers
#endif
Linux version has a kludge file to handle JUST what I use types for wrapped in #if !canImport(UniformTypeIdentifiers)
Extensions
Date
Date
: only func ISO8601Format() -> String
, not needed for APITizer
, used in TrunkLine
#if os(Linux)
import Foundation
extension Date {
func ISO8601Format() -> String {
let formatter = ISO8601DateFormatter()
return formatter.string(from: self)
}
}
#endif
FileIO
Updated helper struct
to deal with URL
problems.
- `initializer URL(filePath: filePath)` is still `URL(fileURLWithPath: filePath)`
- `.appending(component: folderName)` doesn't exist
I broke the pattern with this and did if statements instead of implementing the extension. If still a thing post WWDC, will refactor.
static func makeFileURL(filePath:String) -> URL {
//TODO: For iOS??
//let locationToWrite = URL.documentsDirectory.appendingPathComponent("testImage", conformingTo: .png)
#if os(Linux)
return URL(fileURLWithPath: filePath)
#else
if #available(macOS 13.0, *) { //don't know how to use this with linux check?
return URL(filePath: filePath)
} else {
// Fallback on earlier versions
return URL(fileURLWithPath: filePath)
}
#endif
}
URLSession
URLSession
: add async convenience inits, with the exclusion of bytes (TODO if not added after WWDC)
Example:
public extension URLSession {
func upload(for request: URLRequest, from bodyData:Data, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) {
var dataTask: URLSessionDataTask?
let onCancel = { dataTask?.cancel() }
var rlocal = request
rlocal.httpMethod = "POST"
rlocal.httpBody = bodyData
return try await withTaskCancellationHandler(
operation: {
try await withCheckedThrowingContinuation { continuation in
dataTask = self.dataTask(with: rlocal) { data, response, error in
guard let data = data, let response = response else {
let error = error ?? URLError(.badServerResponse)
return continuation.resume(throwing: error)
}
continuation.resume(returning: (data, response))
}
dataTask?.resume()
}
},
onCancel: {
onCancel()
}
)
}
}
Glue Implementations
UTType
UTType library doesn’t not exist on Linux. Wrote BARE-MINIUM hard coded make it work for image file detection. It is a mess and not worth sharing here, but you can go see it https://github.com/carlynorama/APIng/blob/for_linux_test/Sources/APIng/Linux%2BUTType.swift
Actually… that’s an Improvement
In POSTEncoders
) discovered new-to-me non-objectiveC way to detect Optionalfunc makeDictionary(from itemToEncode:Any) -> [String:String]?
. Both ways are in APIng, only the new way got moved to APITizer
.
if child.value is ExpressibleByNilLiteral {
switch child.value {
case Optional<Any>.none: print("Nil!")
default:
print("Not nil!")
let (_, some) = Mirror(reflecting: child.value).children.first!
//print(some)
dictionary[key] = String(describing: some)
}
// //OLD:
// //https://forums.swift.org/t/calling-object-getclass-on-swift-objects/62790/4
// let typeDescription = object_getClass(child.value)?.description() ?? "" //Not in linux.
// if !typeDescription.contains("Null") && !typeDescription.contains("Empty") {
// let (_, some) = Mirror(reflecting: child.value).children.first!
// //print(some)
// dictionary[key] = String(describing: some)
// }
} else {
dictionary[key] = String(describing: child.value)
}