Contents

CaptureContext/swift-marker-protocols

Marker protocols for common classes in Apple SDKs

Table of contents

- Methods on generic Self - Properties on generic Self - Optionals

Motivation

Marker protocols is an approach of partially erasing type constraints for writing generic extensions for types in Swift.

Such protocols can be required by different packages and declaring them in-place may cause name collisions.

This lightweight package declares a set of core marker protocols to potentially address such collisions.

Examples

Marker protocols are useful for building type-aware generic extensions.

Methods on generic Self

Without MarkerProtocols
extension AVCaptureDevice {
  func withExclusiveLock(
    // ❌ `Self` can't be used on non-final class AVCaptureDevice
    perform configuration: (AVCaptureDevice) async -> Void
  ) async throws {
    try self.lockForConfiguration()
    await configuration(self)
    self.unlockForConfiguration()
  }
}

let instance: CustomAVCaptureDevice = .init()
instance.withExlusiveLock { device in
  // ❌ Type of device is erased to AVCaptureDevice
  // not just base AVCaptureDevice
}
With MarkerProtocols
import AVFoundationMarkerProtocols

extension _AVCaptureDeviceProtocol {
  func withExclusiveLock(
    // ✅ `Self` can be used on a marker protocol
    perform configuration: (Self) async -> Void
  ) async throws {
    try self.lockForConfiguration()
    await configuration(self)
    self.unlockForConfiguration()
  }
}

let instance: CustomAVCaptureDevice = .init()
instance.withExlusiveLock { device in
  // ✅ Type of device is kept - CustomAVCaptureDevice
}

Properties on generic Self

Lets say you want to build some layout proxy for Cocoa views

  • Target API

``swift view.layout.chain.of.calls() ``

  • Chainable proxy type

```swift public struct LayoutProxy<Target: UIView> { public let target: Target

internal init(_ target: Target) { self.target = target } } ```

Without MarkerProtocols

extension UIView {
  // ❌ `Self` can't be used on non-final class UIView
  // Using this property on any type will always
  // erase a type of the view
  var layout: LayoutProxy<UIView> { .init(self) }
}
With MarkerProtocols
extension _UIViewProtocol {
  // ✅ `Self` can be used here and the type
  // of the view is kept at the call site
  var layout: LayoutProxy<Self> { .init(self) }
}

Optionals

struct GenericContainer<Content> {
  var content: Content
}
Without MarkerProtocols
extension GenericContainer {
  // This declaration is completely fine, except
  // of being a bit too verbose
  func unwrapped<Value>(with value: Value) -> GenericContainer<Value>
  where Content == Value {
    .init(content: content ?? value)
  }
  
  // ❌ Won't compile since properties can't be 
  // generic and we can't declare `Value` type here
  var unsafelyUnwrapped: GenericContainer<Value> { /*...*/ }
}
With MarkerProtocols
extension GenericContainer where Content: _OptionalProtocol {
  func unwrapped(with value: Value) -> GenericContainer<Value> {
    .init(with: content._optional ?? value)
  }
  
  var unsafelyUnwrapped: GenericContainer<Content.Wrapped> {
    get { .init(content: content._optional!) }
    set { self.content = newValue.content }
  }
}

Products

SwiftMarkerProtocols
  • _OptionalProtocol<Wrapped>
  • _AnyKeyPathProtocol - alternative to Swift._AppendKeyPath protocol
QuartzCoreMarkerProtocols
  • _CALayerProtocol
  • Exports FoundationMarkerProtocols
AVFoundationMarkerProtocols
  • _AVCaptureDeviceProtocol
  • Exports FoundationMarkerProtocols
FoundationMarkerProtocols
  • _NotificationCenterProtocol
  • Exports SwiftMarkerProtocols
CocoaMarkerProtocols
  • _UIViewProtocol / _NSViewProtocol
  • _UIViewControllerProtocol / _NSViewControllerProtocol
  • Exports FoundationMarkerProtocols

[!TIP]

  • UIKit is basically a CocoaTouch framework
  • AppKit is basically Cocoa framework without CoreData

Both frameworks could have shared Cocoa prefix for their types, so capturecontext/cocoa-aliases package provides Cocoa-prefixed aliases for UI/NS prefixed Cocoa types, it also exports aliased _CocoaViewProtocol and _CocoaViewControllerProtocol marker protocols.

MarkerProtocols

This is an umbrella product that exports all available marker protocols.

Installation

Primary targets for this package are other packages

.package(
  url: "https://github.com/capturecontext/swift-marker-protocols.git", 
  .upToNextMajor(from: "1.1.0")
)

Do not forget about target dependencies:

.product(
  name: "MarkerProtocols", 
  package: "swift-marker-protocols"
)

License

This library is released under the MIT license. See LICENSE for details.

Package Metadata

Repository: CaptureContext/swift-marker-protocols

Stars: 0

Forks: 0

Open issues: 0

Default branch: main

Primary language: swift

License: MIT

Topics: avfoundation, cocoa, foundation, metaprogramming, quartzcore, swift, uikit

README: README.md