Skip to content

kylehughes/Weakify

Repository files navigation

Weakify

Platform Versions Swift Versions Test

A simple, ergonomic solution for safely capturing method references in Swift.

About

Weakify provides convenient and safe mechanisms for capturing method references weakly or unownedly, ensuring closures referencing methods on an object do not inadvertently extend the object's lifetime. It relies on unapplied method references, and uses parameter packs to support heterogeneous argument lists.

This package encourages:

  • Single-line syntax for capturing method references.
  • Avoidance of retain cycles through weak or unowned captures.

Weakify is extremely lightweight and has a robust test suite.

Capabilities

  • Weakly capture method references with automatic fallback values.
  • Unownedly capture method references when you know the target will outlive the closure.
  • Ergonomic handling of heterogenous arguments.
  • Swift 6 language mode support.

Supported Platforms

  • iOS 13.0+
  • macOS 10.15+
  • tvOS 13.0+
  • visionOS 1.0+
  • watchOS 6.0+

Requirements

  • Swift 6.1+
  • Xcode 16.3+

Note

This package ideally would only require Swift 5.9, but compiler versions prior to 6.1 (packaged with Xcode 16.3) have a bug that makes the intended usage incompatible with @MainActor-isolated closures. We require Swift language tools version 6.1 as a proxy for this tribal knowledge.

Documentation

Documentation is available on GitHub Pages.

Installation

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/kylehughes/Weakify.git", .upToNextMajor(from: "1.0.0")),
]

Quick Start

Weakly capture a method reference:

import Weakify

class MyViewController: UIViewController {
    private lazy var button: UIButton = {
        let button = UIButton()
        button.addAction(
            UIAction(handler: weakify(MyViewController.buttonTapped, on: self)),
            for: .primaryActionTriggered
        )
        return button
    }()

    private func buttonTapped(_ action: UIAction) {
        print("Button tapped")
    }
}

Unownedly capture a method reference:

import Weakify

class MyViewController: UIViewController {
    func observe(notificationCenter: NotificationCenter) {
        notificationCenter.addObserver(
            forName: NSNotification.Name("MyNotification"),
            object: nil,
            queue: .main,
            using: disown(MyViewController.handleNotification, on: self)
        )
    }

    private func handleNotification(_ notification: Notification) {
        print("Notification received")
    }
}

Usage

Note

An unapplied method reference (UMR) is the function value produced by writing an instance method on the type instead of an instance, for example UIView.layoutIfNeeded or Array.append. Its curried shape is (Self) -> (Args…) -> Result, so supplying a specific instance—unappliedMethodReference(target)—yields the regular (Args…) -> Result closure you would normally call.

Weak Capture with Fallback

Use weakify to safely capture self without retaining it. A default fallback value can be provided for when self has been deallocated:

let weakHandler = weakify(MyViewController.formatMessage, on: self, default: "N/A")

Weak Capture without Fallback

Use weakify to safely capture self without retaining it. If the accepting closure does not need a return value, you can omit the fallback. No side effects will occur if the target is deallocated.

let weakHandler = weakify(MyViewController.fireAndForget, on: self)

Unowned Capture

Use disown to capture self when the target object will definitely outlive the closure:

let unownedHandler = disown(MyViewController.updateStatus, on: self)

Heterogeneous Arguments

Weakify supports methods with heterogeneous argument lists:

func printMessage(_ prefix: String, count: Int) {
    print("\(prefix): \(count)")
}

let printer = weakify(MyViewController.printMessage, on: self)
printer("Age", 5)

Important Behavior

  • weakify closures evaluate the provided default when the target is deallocated.
  • disown closures will crash if called after the target is deallocated; ensure the target outlives the closure.

Contributions

Weakify is not accepting source contributions at this time. Bug reports will be considered.

Author

Kyle Hughes

Bluesky
LinkedIn
Mastodon

License

Weakify is available under the MIT license.

See LICENSE for details.

About

A simple, ergonomic solution for safely capturing method references in Swift.

Topics

Resources

License

Stars

Watchers

Forks

Languages