Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorators in Swift

I'm new to Swift, and I'd like to know if the language has some equivalent to Python's decorator pattern.
For example:

import functools


def announce(func):
    """Print a function's arguments and return value as it's called."""
    @functools.wraps(func)
    def announced_func(*args, **kwargs):
        rv = func(*args, **kwargs)
        print('In: {0}, {1}'.format(args, kwargs))
        print('Out: {}'.format(rv))
        return rv
    return announced_func


@announce  # add = announce(add)
def add(a, b):
    return a + b

add(2, 5)
# In: (2, 5), {}
# Out: 7
# 7

Perhaps I just haven't found it yet, but Swift doesn't seem to have a way to forward arbitrary arguments to functions or to preserve a wrapped function's information (as functools.wraps does).

Is there an equivalent, or is the pattern not meant to be used in Swift?

like image 854
Noah Avatar asked Oct 20 '16 14:10

Noah


1 Answers

Example above updated for Swift 5.1+

@propertyWrapper
struct Announced<T, U> {
    private let announcedFunction: (T) -> U
    var wrappedValue: (T) -> U { announcedFunction }
    
    init(wrappedValue: @escaping (T) -> U) {
        announcedFunction = { args in
            let rv = wrappedValue(args)
            print("In: \(args)")
            print("Out: \(rv)")
            return rv
        }
    }
}

struct Test {
    @Announced static var add: ((Int, Int)) -> Int = { $0.0 + $0.1 }
    @Announced static var multiply: ((Int, Int)) -> Int = { $0.0 * $0.1 }
    @Announced static var length : (String) -> Int = { (str: String) in str.count }
    @Announced static var greet: (String) -> String = { "Hello, \($0)" }
}

Test.add((2, 5)) // In: (2, 5)\n Out: 7
Test.multiply((2, 5)) // In: (2, 5)\n Out: 10
Test.length("Hello world!") // In: Hello world!\n Out: 12
Test.greet("Paul") // In: Paul\n Out: Hello, Paul

[Edit] Actually, it can be simplified:

@propertyWrapper
struct Announced<T, U> {
    let wrappedValue: (T) -> U

    init(wrappedValue: @escaping (T) -> U) {
        self.wrappedValue = { args in
            let rv = wrappedValue(args)
            print("In: \(args)")
            print("Out: \(rv)")
            return rv
        }
    }
}
like image 67
Roman Avatar answered Sep 25 '22 23:09

Roman