Contents

cocoanetics/swiftcross

Small, dependency-free compatibility shims so the **same Swift source compiles

The problem

A lot of convenient Foundation API only exists on Apple's Foundation, not on the open-source swift-corelibs-foundation used on Linux/Windows/Android. The moment a cross-platform package touches one of these, it stops building elsewhere:

// Compiles on Apple. On Linux: "value of type 'URLSession' has no member 'bytes'"
let (bytes, response) = try await URLSession.shared.bytes(for: request)
for try await line in bytes.lines {  }

// Compiles on Apple. On Linux: "cannot find type 'UTType' in scope"
let mime = UTType(filenameExtension: "png")?.preferredMIMEType

The usual fix is to scatter #if canImport(FoundationNetworking) shims through every project that hits the gap. SwiftCross collects those shims in one place so you write the API once and import a single module.

Approach

import SwiftCross is a drop-in replacement for import Foundation. It re-exports Foundation (plus FoundationNetworking where that's a separate module, and UniformTypeIdentifiers where it exists) and layers the shims on top. On a platform that already has the real API, SwiftCross gets out of the way and you get the native implementation; on one that doesn't, you get the shim — same call site either way.

Installation

// Package.swift
dependencies: [
    .package(url: "https://github.com/Cocoanetics/SwiftCross.git", from: "1.0.0"),
],
targets: [
    .target(name: "MyLibrary", dependencies: ["SwiftCross"]),
]
import SwiftCross   // instead of: import Foundation

What's included

URLSession.bytes(for:) / bytes(from:) + AsyncBytes

Apple's async byte-streaming API, ported to FoundationNetworking. This is a real incremental stream — a one-shot URLSession data delegate forwards chunks as they arrive and resolves the response the moment headers land, so Server-Sent Events and other long-lived responses work, not just small bodies.

let (bytes, response) = try await URLSession.shared.bytes(for: request)
for try await line in bytes.lines {
    print(line)   // streams as the server sends it, on Linux/Windows too
}

UTType

A minimal stand-in for UniformTypeIdentifiers.UTType covering the filename-extension ↔ MIME-type mapping that portable code needs.

UTType(filenameExtension: "png")?.preferredMIMEType        // "image/png"
UTType(mimeType: "application/json")?.preferredFilenameExtension  // "json"

On Apple platforms the real UTType is re-exported unchanged, so its full UTI hierarchy (conformances, supertypes, system-declared types) stays available there; the shim intentionally models only the extension/MIME surface.

ProcessInfo.localIPAddress

The machine's primary routable (non-loopback, non-link-local) IP address — something Foundation has no portable API for. A companion to the built-in, already-portable ProcessInfo.hostName (which needs no shim). Implemented on every platform: Apple / Linux / Android enumerate interfaces with getifaddrs (preferring a routable IPv4); Windows, which has no getifaddrs, opens a UDP socket and reads back the OS-selected source address with getsockname (no packet is sent).

let host = ProcessInfo.processInfo.hostName        // built-in, works everywhere
let ip   = ProcessInfo.processInfo.localIPAddress  // SwiftCross; nil if no active interface

String.Encoding(ianaCharsetName:)

Resolve an IANA charset label ("utf-8", "ISO-8859-1", "windows-1252", "shift_jis", …) to a String.Encoding, with normalization and alias folding. Uses CoreFoundation's full IANA table on Apple platforms and a built-in table elsewhere.

let data =
let encoding = String.Encoding(ianaCharsetName: charsetFromHeader) ?? .utf8
let text = String(data: data, encoding: encoding)

Platform support

| Platform | Status | Notes | | --- | --- | --- | | macOS / iOS / tvOS / watchOS / visionOS | ✅ build + test | Native APIs re-exported | | Linux | ✅ build + test | Primary shim target (swift-corelibs-foundation) | | Windows | ✅ build + test | localIPAddress via a UDP socket (no getifaddrs) | | Android | ✅ build + test | Unit tests run on the emulator (API 28) |

Every platform is exercised in CI on each push.

Contributing

The bar for a shim: it should let the same source compile and run across platforms, be dependency-free, and re-export/defer to the real API where it already exists. Add the implementation under Sources/SwiftCross/, add a cross-platform test (assert behaviour that holds on both the native and shimmed paths — see Tests/SwiftCrossTests), and make sure all five CI legs stay green.

License

MIT — see LICENSE.

Package Metadata

Repository: cocoanetics/swiftcross

Default branch: main

README: README.md