👷‍♀️ Closure-based delegation without memory leaks

Related tags

Utility Delegated
Overview

Delegated 2.0

Delegated is a super small package that helps you avoid retain cycles when using closure-based delegation.

New Medium post here.

Original Medium post (Delegated 0.1.2) here.

🚨 WARNING! Delegated 2.0 is not compatible with Delegated 0.1.2. If you don't want to migrate your current codebase, stay on Delegated 0.1.2. See documentation for Delegated 0.1.2 here. If you need any help migrating from 0.1.x to 2.0.x, please open an issue.

Usage

Before:

final class TextField {
    var didUpdate: (String) -> () = { _ in }
}

// later...

self.textField.didUpdate = { [weak self] text in
    guard let strongSelf = self else {
        return
    }
    strongSelf.label.text = text
}

After:

final class TextField {
    @Delegated var didUpdate: (String) -> ()
}

// later...

textField.$didUpdate.delegate(to: self) { (self, text) in
    // `self` is weak automatically!
    self.label.text = text
}

No retain cycles! No memory leaks! No [weak self]! 🎉

Guide

Creating a delegated function

final class TextField {
    @Delegated var didUpdate: (String) -> ()
}

This will only compile for closures that have exactly one argument and no return value. To use any other number of arguments, use this:

final class TextField {
    @Delegated0 var didStartEditing: () -> Void
    @Delegated1 var didUpdate: (String) -> Void
    @Delegated2 var didReplace: (String, String) -> Void
}

Delegated0 - Delegated4 are provided out of the box. Delegated is a typealias for Delegated1.

Registering as a delegate

// somewhere inside init() or viewDidLoad() or similar
self.textField = TextField()
textField.$didUpdate.delegate(to: self) { (self, text) in
    self.label.text = text
}

By default, delegate(to:with:) will wrap self in a weak reference so that you don't need to remember to write [weak self] every time. This is the main feature of Delegated, but if this is not the behavior you want, you can use manuallyDelegate(with:):

// somewhere inside init() or viewDidLoad() or similar
self.textField = TextField()
textField.$didUpdate.manuallyDelegate { (text) in
    print(text)
}

Calling a delegate

final class TextField {
    @Delegated var didUpdate: (String) -> Void
    
    /// ...
    
    private func didFinishEditing() {
        self.didUpdate(self.text)
    }
}

Delegating a function with a return value

If your delegated function is designed to have a return value (non-Void), use @ReturningDelegated wrapper.

final class TextField {
    @ReturningDelegated  var shouldReturn: (String) -> Bool?
    
    @ReturningDelegated0 var shouldBeginEditing: () -> Bool?
    @ReturningDelegated2 var shouldReplace: (String, String) -> Bool?
}

// ...

textField.$shouldReturn.delegate(to: self) { (self, string) -> Bool in
    if string.count > 5 {
        return true
    } else {
        return false
    }
}

IMPORTANT: Make sure that your @ReturningDelegated function returns an optional. It will return nil if no delegate is set.

Default @ReturningDelegated supports exactly one input argument. Use @ReturningDelegated0 - @ReturningDelegated4 if you need a different number of arguments (see above).

Removing a delegate

@Delegated var didUpdate: (String) -> ()
// ...
self.$didUpdate.removeDelegate()

Installation

Swift Package Manager

Delegated is officially available only via Swift Package Manager.

In Xcode 11 or greater, in you project, select: File > Swift Packages > Add Pacakage Dependency

In the search bar type

https://github.com/dreymonde/Delegated

and when you find the package, with the next button you can proceed with the installation.

If you can't find anything in the panel of the Swift Packages you probably haven't added yet your github account. You can do that under the Preferences panel of your Xcode, in the Accounts section.

For command-line based apps, you can just add this directly to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/dreymonde/Delegated", from: "2.1.0"),
]

Manual

Of course, you always have an option of just copying-and-pasting the code - Delegated is just one file, so feel free.

You might also like...
A Swift app, named 'iPose', for iPhone's pose measurement based on Swift.

iPhone's pose measurement based on Swift. This is a Swift app, named 'iPose', for iPhone's pose measurement based on Swift. This is a side project to

Find memory issues & leaks in your iOS app without instruments
Find memory issues & leaks in your iOS app without instruments

HeapInspector Find memory issues & leaks in your iOS app HeapInspector is a debug tool that monitors the memory heap with backtrace recording in your

Find memory leaks in your iOS app at develop time.

中文介绍 | FAQ中文 MLeaksFinder MLeaksFinder helps you find memory leaks in your iOS apps at develop time. It can automatically find leaks in UIView and UIV

SwiftyTimer allows you to instantly schedule delays and repeating timers using convenient closure syntax. It's time to get rid of Objective-C cruft.

SwiftyTimer Modern Swifty API for NSTimer SwiftyTimer allows you to instantly schedule delays and repeating timers using convenient closure syntax. It

UIMenuItem with image and closure(block) action
UIMenuItem with image and closure(block) action

MenuItemKit MenuItemKit provides image and block(closure) support for UIMenuItem. MenuItemKit is a Swift project but Objective-C is supported without

McPicker is a customizable, closure driven UIPickerView drop-in solution with animations that is rotation ready.
McPicker is a customizable, closure driven UIPickerView drop-in solution with animations that is rotation ready.

McPicker About McPicker is a UIPickerView drop-in solution with animations that is rotation ready. The more string arrays you pass, the more picker co

NearMe Locations iOS App Using MVVM with Closure
NearMe Locations iOS App Using MVVM with Closure

NearMe Locations iOS App App display information about nearby places around user using user’s current location specified by Latitude and Longitude Imp

An extreme queuing system with high performance for managing all task in app with closure

SwiftyTask An extreme queuing system with high performance for managing all task in app with closure Task Method Tasking of queued closure on GCD (Gra

An extreme queuing system with high performance for managing all task in app with closure

SwiftyTask An extreme queuing system with high performance for managing all task in app with closure Task Method Tasking of queued closure on GCD (Gra

CleanClosureXcode - An Xcode Source Editor extension to clean the closure syntax.
CleanClosureXcode - An Xcode Source Editor extension to clean the closure syntax.

Clean Closure - Xcode Source Editor Extension Clean Closure is a simple Xcode Source Editor Extension for Xcode 8. It parses each line of a file to si

Swift extension which adds start, animating and completion closures for CAAnimation objects. Aka, CAAnimation + Closure / Block
Swift extension which adds start, animating and completion closures for CAAnimation objects. Aka, CAAnimation + Closure / Block

Swift-CAAnimation-Closure Swift extension which adds start, animating and completion closures for CAAnimation objects. Aka, CAAnimation + Closure or C

ClosureReactor - Closure Reactor With Swift

ClosureReactor Example To run the example project, clone the repo, and run pod i

Fast sorted collections for Swift using in-memory B-trees
Fast sorted collections for Swift using in-memory B-trees

Fast Sorted Collections for Swift Using In-Memory B-Trees Overview Reference Documentation Optimizing Collections: The Book What Are B-Trees? Why In-M

MemoryCache - type-safe, thread-safe memory cache class in Swift

MemoryCache is a memory cache class in swift. The MemoryCache class incorporates LRU policies, which ensure that a cache doesn’t

LifetimeTracker can surface retain cycle / memory issues right as you develop your application
LifetimeTracker can surface retain cycle / memory issues right as you develop your application

LifetimeTracker Bar style Circular style LifetimeTracker can surface retain cycle / memory issues right as you develop your application, and it will s

🖐 Memory game with hand gesture recognition that will keep your brain in a good shape!
🖐 Memory game with hand gesture recognition that will keep your brain in a good shape!

Hands I have always been interested in how I can improve my memory in addition to reading books, and once I came across an interesting technique relat

An extremely high-performance, lightweight, and energy-efficient pure Swift async web image loader with memory and disk caching for iOS and  Watch.

KFSwiftImageLoader KFSwiftImageLoader is an extremely high-performance, lightweight, and energy-efficient pure Swift async web image loader with memor

A Protocol-Oriented NotificationCenter which is type safe, thread safe and with memory safety
A Protocol-Oriented NotificationCenter which is type safe, thread safe and with memory safety

A Protocol-Oriented NotificationCenter which is type safe, thread safe and with memory safety. Type Safe No more userInfo dictionary and Downcasting,

Simple memory game written in Swift 4 using VIPER Architecture.
Simple memory game written in Swift 4 using VIPER Architecture.

Viper Memory Game Simple memory game written in Swift 4.2 using VIPER Architecture. The Memory Game is a deck of cards where the user needs to find ma

Comments
  • use generic function to modify the api

    use generic function to modify the api

    There are a lot of Classes like Delegated0, Delegated1, Delegated2... Now ,we can use generic to modify this . You only need to use one class ‘Delegated’ to do all of the above The PropertyWrappe @Delegated() replace all of @Delegated1, @Delegated2, @Delegated3...

    opened by GodL 1
  • Installation via Carthage not possible –

    Installation via Carthage not possible – "has no shared framework schemes"

    I'm getting this error when specifying Delegated in my Cartfile as documented in the README:

    *** Skipped building Delegated due to the error:
    Dependency "Delegated" has no shared framework schemes
    
    opened by Jeehut 1
Releases(2.2.0)
Owner
Oleg Dreyman
WWDC 2018 Scholarship Winner. iOS Engineer @nicephoton.
Oleg Dreyman
Ethereum Wallet Toolkit for iOS - You can implement an Ethereum wallet without a server and blockchain knowledge.

Introduction EtherWalletKit is an Ethereum Wallet Toolkit for iOS. I hope cryptocurrency and decentralized token economy become more widely adapted. H

Sung Woo Chang 136 Dec 25, 2022
Tweak your iOS app without recompiling!

SwiftTweaks Adjust your iOS app on the fly without waiting to re-compile! Your users won’t see your animation study, Sketch comps, or prototypes. What

Khan Academy 1.4k Dec 28, 2022
Swift code to programmatically execute local or hosted JXA payloads without using the on-disk osascript binary

Swift code to programmatically execute local or hosted JXA payloads without using the on-disk osascript binary. This is helpful when you have Terminal access to a macOS host and want to launch a JXA .js payload without using on-disk osascript commands.

Cedric Owens 20 Sep 27, 2022
Tweak your iOS app without recompiling!

SwiftTweaks Adjust your iOS app on the fly without waiting to re-compile! Your users won’t see your animation study, Sketch comps, or prototypes. What

Khan Academy 1.4k Dec 28, 2022
Project shows how to unit test asynchronous API calls in Swift using Mocking without using any 3rd party software

UnitTestingNetworkCalls-Swift Project shows how to unit test asynchronous API ca

Gary M 0 May 6, 2022
📦 KeyPath dynamicMemberLookup based syntax sugar for Swift.

DuctTape ?? KeyPath dynamicMemberLookup based syntax sugar for Swift. let label: UILabel = UILabel().ductTape .numberOfLines(0) .textColor(.re

Taiki Suzuki 171 Nov 4, 2022
RResultBuilder is DSL library based on Result Builder

RResultBuilder is DSL library based on Result Builder Features Requirements Installation Usage Screenshot Example Contribute Meta Feat

Rakuten, Inc. 24 Sep 28, 2022
Automatically set your keyboard's backlight based on your Mac's ambient light sensor.

QMK Ambient Backlight Automatically set your keyboard's backlight based on your Mac's ambient light sensor. Compatibility macOS Big Sur or later, a Ma

Karl Shea 29 Aug 6, 2022
Proper YAML support for Objective-C. Based on recommended libyaml.

YAML.framework for Objective-C Based on C LibYAML library (http://pyyaml.org/wiki/LibYAML) by Kirill Simonov. YAML.framework provides support for YAML

Mirek Rusin 236 Aug 29, 2022
DGLabelSize - Functions that calculate the size of uilabel based on different string lengths

DGLabelSize Functions that calculate the size of uilabel based on different stri

donggyu 5 Jun 10, 2022