swift-tips icon indicating copy to clipboard operation
swift-tips copied to clipboard

Suggestions

Open DanielSwift1992 opened this issue 5 years ago • 2 comments

#45 Getting rid of overabundant [weak self] and guard

Just a little bit another way with the same example

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

postfix operator ~
public postfix func ~ <T>(value: T) -> T.Type {
    return T.self
}

extension NSObjectProtocol {
    func weak<T>(_ closure: @escaping (Self) -> (T) -> Void) -> (T) -> Void {
        return { [weak self] in
            guard let self = self else { return }
            return closure(self)($0)
        }
    }
}

class Producer: NSObject {
    
    deinit {
        print("deinit Producer")
    }
    
    private var handler: (Int) -> Void = { _ in }
    
    func register(handler: @escaping (Int) -> Void) {
        self.handler = handler
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: { self.handler(42) })
    }
}

class Consumer: NSObject {
    
    deinit {
        print("deinit Consumer")
    }
    
    let producer = Producer()
    
    func consume() {
        producer.register(handler: weak(self~ .handle))
    }
    
    private func handle(_ result: Int) {
        print("🎉 \(result)")
    }
}

var consumer: Consumer? = Consumer()

consumer?.consume()

DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: { consumer = nil })

// This code prints:
// 🎉 42
// deinit Consumer
// deinit Producer

However sometimes you need function to return value and weak will not work. You might want to use unowned:

extension NSObjectProtocol {
    func unowned<T, U>(_ closure: @escaping (Self) -> (T) -> U) -> (T) -> U {
        return { [ unowned self] in
            return closure(self)($0)
        }
    }
}

#07 Implementing the builder pattern with keypaths

Not using keyPath allows to call functions:

import UIKit

extension NSObjectProtocol {
    @discardableResult
    func apply(_ closure: (Self) -> () ) -> Self {
    { closure(self) }()
        return self
    }
}

let view = UIView()

let label = UILabel().apply {
    $0.textColor = .red
    $0.text = "Foo \n Foo"
    $0.textAlignment = .right
    $0.layer.cornerRadius = 5
    
    $0.sizeToFit()
}

view.addSubview(label)

It is safe because it nonescaping closure.

DanielSwift1992 avatar Jan 13 '19 13:01 DanielSwift1992

Hello, thank you for those suggestions!

I understand that the operator ~ can have value in the context of a project. However, custom operators are a controversial topic, so I wouldn't advise its use in the general case. But I feel that it could alternatively implemented as a computed property on NSObject, and you'd get to use it the same way than the operator.

The apply function is indeed interesting! If I remember well, I think it's actually part of the standard library in Kotlin 🙂

vincent-pradeilles avatar Jan 27 '19 18:01 vincent-pradeilles

Yes, but unfortunately in swift you can't get rid of "$0". At list I do not know how to do that =)

DanielSwift1992 avatar Feb 11 '19 08:02 DanielSwift1992