A Swift package that provides cross-platform type aliases and utilities for building applications that work seamlessly across iOS, macOS, tvOS, and watchOS.
XPlatform simplifies cross-platform Swift development by providing unified type aliases and convenience methods that abstract away platform-specific differences between AppKit (macOS) and UIKit (iOS/tvOS/watchOS).
📚 New to XPlatform? Check out our Getting Started Guide to understand the problems XPlatform solves and see real-world examples.
Add XPlatform to your project by adding the following to your Package.swift:
dependencies: [
.package(url: "https://github.com/yourusername/XPlatform.git", from: "1.0.0")
]Or add it through Xcode:
- File → Add Package Dependencies
- Enter the repository URL
- Select the version requirements
- iOS 15.0+
- macOS 12.0+
- tvOS 15.0+
- watchOS 8.0+
- Swift 5.7+
XPlatform provides unified type aliases that map to the appropriate platform-specific types:
| XPlatform Type | macOS (AppKit) | iOS/tvOS/watchOS (UIKit) |
|---|---|---|
XView |
NSView |
UIView |
XViewController |
NSViewController |
UIViewController |
XWindow |
NSWindow |
UIWindow |
XImage |
NSImage |
UIImage |
XColor |
NSColor |
UIColor |
XFont |
NSFont |
UIFont |
XBezierPath |
NSBezierPath |
UIBezierPath |
XGestureRecognizer |
NSGestureRecognizer |
UIGestureRecognizer |
XTapGestureRecognizer |
NSClickGestureRecognizer |
UITapGestureRecognizer |
XPanGestureRecognizer |
NSPanGestureRecognizer |
UIPanGestureRecognizer |
XCollectionView |
NSCollectionView |
UICollectionView |
XCollectionViewDelegate |
NSCollectionViewDelegate |
UICollectionViewDelegate |
XCollectionViewDataSource |
NSCollectionViewDataSource |
UICollectionViewDataSource |
XViewControllerRepresentable |
NSViewControllerRepresentable |
UIViewControllerRepresentable |
XViewRepresentable |
NSViewRepresentable |
UIViewRepresentable |
XMenu |
NSMenu |
UIMenu |
XCollectionViewDiffableDataSource |
NSCollectionViewDiffableDataSource |
UICollectionViewDiffableDataSource |
XCollectionViewLayout |
NSCollectionViewLayout |
UICollectionViewLayout |
XAlert |
NSAlert |
UIAlertController |
XPasteboard |
NSPasteboard |
UIPasteboard |
XResponder |
NSResponder |
UIResponder |
extension CGRect {
func transform(to targetRect: CGRect) -> CGAffineTransform
}transform(to:): Creates a transform that maps this rectangle to the target rectangle- Parameters:
targetRect- The destination rectangle to transform to - Returns: A
CGAffineTransformthat maps this rectangle to the target rectangle - Use Case: Useful for scaling and positioning content between different coordinate spaces
- Parameters:
extension XView {
func setNeedsLayout()
var usesFlippedCoordinates: Bool { get }
func makeFirstResponder()
func resignFirstResponder()
func addTapGesture(target: Any?, action: Selector)
func addPanGesture(target: Any?, action: Selector)
func addContextMenu(_ menu: XMenu) // macOS
func addContextMenu(provider: @escaping () -> XMenu?) // iOS 13.0+
}setNeedsLayout(): Marks the view as needing layout- Platform Notes: On macOS, this sets
needsLayout = true. On iOS/tvOS/watchOS, the native method is already available
- Platform Notes: On macOS, this sets
usesFlippedCoordinates: Returns whether the view uses a flipped coordinate system- Returns:
trueif the coordinate system origin is at top-left,falseif at bottom-left - Platform Notes: iOS always returns
true, macOS returns the value ofisFlipped
- Returns:
makeFirstResponder(): Makes this view the first responder- Platform Notes: On macOS uses
window?.makeFirstResponder(self), on iOS usesbecomeFirstResponder()
- Platform Notes: On macOS uses
resignFirstResponder(): Resigns first responder status- Platform Notes: Handles platform differences in resigning first responder
addTapGesture(target:action:): Adds a tap/click gesture recognizer- Platform Notes: Uses
NSClickGestureRecognizeron macOS,UITapGestureRecognizeron iOS
- Platform Notes: Uses
addTapGesture(target:action:taps:): Adds a tap/click gesture recognizer with specific tap count- Parameters:
taps- Number of taps required (e.g., 2 for double-tap) - Platform Notes: Sets
numberOfClicksRequiredon macOS,numberOfTapsRequiredon iOS
- Parameters:
addPanGesture(target:action:): Adds a pan gesture recognizer
addContextMenu(_:)(macOS): Adds a context menu to the viewaddContextMenu(provider:)(iOS 13.0+): Adds a context menu with a provider closure
extension XResponder {
func findResponder<T>(of type: T.Type) -> T?
var next: XResponder? { get } // macOS only - unified API
var responders: [XResponder] { get }
}-
findResponder(of:): Finds the first responder of the specified type in the responder chain- Parameters:
type- The type of responder to search for - Returns: The first responder matching the type, or
nilif not found - Use Case: Navigate the responder chain to find specific view controllers or views
- Parameters:
-
next: Returns the next responder in the chain (unified API for iOS/macOS)- Platform Notes: On macOS, provides a unified interface matching iOS's
nextproperty
- Platform Notes: On macOS, provides a unified interface matching iOS's
-
responders: Returns all responders in the chain as an array- Returns: An array containing all responders from the current responder up the chain
- Use Case: Useful for debugging or iterating through the entire responder chain
XResponder conforms to Sequence protocol, allowing iteration through the responder chain:
for responder in view {
print(type(of: responder))
}extension XImage {
func jpegData(compressionQuality: CGFloat) -> Data?
}- Description: Converts the image to JPEG data with the specified compression quality
- Parameters:
compressionQuality: A value between 0.0 (maximum compression) and 1.0 (no compression)
- Returns: JPEG data representation of the image, or
nilif conversion fails - Platform Notes: Only available on macOS; iOS/tvOS/watchOS already provide this method natively
extension Image {
init(_ image: XImage)
}- Description: Creates a SwiftUI
Imagefrom a platform-specific image - Platform Notes: Uses
init(nsImage:)on macOS andinit(uiImage:)on other platforms
extension View {
func linkButtonStyle() -> some View
}- Description: Applies a link-style button appearance
- Platform Behavior:
- macOS: Uses
.buttonStyle(.link) - iOS/tvOS/watchOS: Uses
.buttonStyle(.plain)with blue foreground color
- macOS: Uses
extension View {
func primaryButtonStyle() -> some View
}- Description: Applies a prominent button style suitable for primary actions
- Platform Behavior: Uses
.borderedProminenton all platforms
extension View {
func secondaryButtonStyle() -> some View
}- Description: Applies a bordered button style suitable for secondary actions
- Platform Behavior: Uses
.borderedon all platforms
extension XFont {
static func xSystemFont(ofSize size: CGFloat, weight: XFont.Weight) -> XFont
static var xSystemFontSize: CGFloat { get }
static var xSmallSystemFontSize: CGFloat { get }
static var xLabelFontSize: CGFloat { get }
}xSystemFont(ofSize:weight:): Creates a system font with the specified size and weightxSystemFontSize: Returns the default system font sizexSmallSystemFontSize: Returns the default small system font sizexLabelFontSize: Returns the default label font size- Note: Methods are prefixed with 'x' to avoid conflicts with native properties
extension XPasteboard {
static var xGeneral: XPasteboard { get }
var xString: String? { get set }
}xGeneral: Returns the general/system pasteboardxString: Gets or sets the string content of the pasteboard- Note: Properties are prefixed with 'x' to avoid conflicts with native properties
struct XAlertStyle {
static let informational = 0
static let warning = 1
static let critical = 2
}
extension XAlert {
static func showAlert(title: String, message: String, style: Int = XAlertStyle.informational)
}showAlert(title:message:style:): Shows a simple alert with a message- Platform Notes: On macOS, displays a modal alert. On iOS, prints to console (would need view controller for proper presentation)
struct XPlatform {
// Colors
static let primaryBackgroundColor: XColor
static let secondaryBackgroundColor: XColor
static let tertiaryBackgroundColor: XColor
static var adaptiveTextBackgroundColor: XColor { get }
static var labelColor: XColor { get }
static var secondaryLabelColor: XColor { get }
static var separatorColor: XColor { get }
// File System
static var documentsDirectory: URL { get }
static var applicationSupportDirectory: URL { get }
static var cachesDirectory: URL { get }
static var temporaryDirectory: URL { get }
}| Property | macOS | iOS/tvOS/watchOS |
|---|---|---|
primaryBackgroundColor |
NSColor.controlBackgroundColor |
UIColor.systemBackground |
secondaryBackgroundColor |
NSColor.windowBackgroundColor |
UIColor.secondarySystemBackground |
tertiaryBackgroundColor |
NSColor.controlBackgroundColor |
UIColor.tertiarySystemBackground |
adaptiveTextBackgroundColor |
NSColor.textBackgroundColor |
UIColor.systemBackground |
labelColor |
NSColor.labelColor |
UIColor.label |
secondaryLabelColor |
NSColor.secondaryLabelColor |
UIColor.secondaryLabel |
separatorColor |
NSColor.separatorColor |
UIColor.separator |
documentsDirectory: Returns the user's documents directory URLapplicationSupportDirectory: Returns the application support directory URLcachesDirectory: Returns the caches directory URLtemporaryDirectory: Returns the temporary directory URL
import XPlatform
import CoreGraphics
let sourceRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let targetRect = CGRect(x: 50, y: 50, width: 200, height: 200)
// Create a transform that maps source to target
let transform = sourceRect.transform(to: targetRect)
// Apply the transform to points or paths
let point = CGPoint(x: 50, y: 50)
let transformedPoint = point.applying(transform)import XPlatform
class MyView: XView {
func findParentViewController() {
// Find the nearest view controller in the responder chain
if let viewController = self.findResponder(of: XViewController.self) {
print("Found parent view controller: \(viewController)")
}
// Iterate through all responders
for responder in self.responders {
print("Responder: \(type(of: responder))")
}
// Using sequence conformance
let controllers = self.compactMap { $0 as? XViewController }
print("Found \(controllers.count) view controllers in chain")
}
}import XPlatform
// Works on all platforms
let view = XView()
let color = XColor.red
let image = XImage()
// SwiftUI integration
struct ContentView: View {
var body: some View {
Image(myXImage)
.resizable()
Button("Primary Action") {
// Action
}
.primaryButtonStyle()
}
}import XPlatform
class MyViewController: XViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = XPlatform.primaryBackgroundColor
// Your cross-platform code here
}
}import XPlatform
func processImage(_ image: XImage) -> Data? {
// This works on all platforms
#if os(macOS)
return image.jpegData(compressionQuality: 0.8)
#else
return image.jpegData(compressionQuality: 0.8)
#endif
}import XPlatform
class InteractiveView: XView {
override init(frame: CGRect) {
super.init(frame: frame)
// Add single tap gesture
self.addTapGesture(target: self, action: #selector(handleTap))
// Add double tap gesture
self.addTapGesture(target: self, action: #selector(handleDoubleTap), taps: 2)
// Add triple tap gesture
self.addTapGesture(target: self, action: #selector(handleTripleTap), taps: 3)
// Add pan gesture
self.addPanGesture(target: self, action: #selector(handlePan))
}
@objc func handleTap(_ gesture: XTapGestureRecognizer) {
print("Single tap!")
}
@objc func handleDoubleTap(_ gesture: XTapGestureRecognizer) {
print("Double tap!")
}
@objc func handleTripleTap(_ gesture: XTapGestureRecognizer) {
print("Triple tap!")
}
@objc func handlePan(_ gesture: XPanGestureRecognizer) {
print("View panned!")
}
}import XPlatform
// Create system fonts
let titleFont = XFont.xSystemFont(ofSize: 24, weight: .bold)
let bodyFont = XFont.xSystemFont(ofSize: XFont.xSystemFontSize, weight: .regular)
let smallFont = XFont.xSystemFont(ofSize: XFont.xSmallSystemFontSize, weight: .light)import XPlatform
// Copy text to pasteboard
let pasteboard = XPasteboard.xGeneral
pasteboard.xString = "Hello, Cross-Platform!"
// Read text from pasteboard
if let copiedText = pasteboard.xString {
print("Pasteboard contains: \(copiedText)")
}import XPlatform
// Access common directories
let documentsURL = XPlatform.documentsDirectory
let cachesURL = XPlatform.cachesDirectory
let tempURL = XPlatform.temporaryDirectory
// Save a file to documents
let fileURL = documentsURL.appendingPathComponent("data.txt")
try "Hello, World!".write(to: fileURL, atomically: true, encoding: .utf8)import XPlatform
import SwiftUI
struct ThemedView: View {
var body: some View {
VStack {
Text("Primary Label")
.foregroundColor(Color(XPlatform.labelColor))
Text("Secondary Label")
.foregroundColor(Color(XPlatform.secondaryLabelColor))
Divider()
.background(Color(XPlatform.separatorColor))
}
.background(Color(XPlatform.adaptiveTextBackgroundColor))
}
}Contributions are welcome! Please feel free to submit a Pull Request.
This package is available under the MIT license. See the LICENSE file for more info.