Elye
Elye

Reputation: 60081

Is there an Kotlin equivalent `with` function in Swift?

In Kotlin, we could change the below

// Original code
var commonObj = ClassCommonObj()
 commonObj.data1 = dataA
 commonObj.data2 = dataB
 commonObj.data3 = dataC

// Improved code
var commonObj = ClassCommonObj()
with(commonObj) {
    data1 = dataA
    data2 = dataB
    data3 = dataC
}

However in Swift as below, do I have equivalent with function to use?

// Original code
var commonObj = ClassCommonObj()
 commonObj.data1 = dataA
 commonObj.data2 = dataB
 commonObj.data3 = dataC

Upvotes: 13

Views: 2843

Answers (2)

Clay Bridges
Clay Bridges

Reputation: 11880

Like @Hexfire said, so far, no built-in Swift equivalent to Kotlin's with(). As he points out, you can more or less write one yourself.

I use a version slightly different than the Kotlin with() that automatically returns the modified element (more akin to Kotlin's apply). I find this clearer, pithier, and more generally useful.

This is the version I use:

@discardableResult
public func with<T>(_ item: T, _ closure: (inout T) -> Void) -> T {
  var mutableItem = item
  closure(&mutableItem)
  return mutableItem
}

It's declared globally (so no dependency on NSObject or extension declarations). It also handles mutability like I expect. In use, it looks like:

let myWellDescribedLabel = with(UILabel()) {
  $0.attributedText = attributedStringTitle
  $0.isAccessibilityElement = true
  $0.numberOfLines = 1
}

Unfortunately (or is it? see comments), Swift does not have self syntax in closures, so you must reference the passed object as $0 (or create a named parameter to the closure).

While we're here, a withLet() that handles optionals is also very useful:

@discardableResult
public func withLet<T>(_ item: Optional<T>, _ closure: (inout T) -> Void) -> Optional<T> {
  guard let item = item else { return nil }
  return with(item, closure)
}

These are in a gist here.

Upvotes: 1

Hexfire
Hexfire

Reputation: 6058

Unfortunately, no such functionality so far in Swift. However, similar functionality can be reached with the power of extensions:

protocol ScopeFunc {}
extension ScopeFunc {
    @inline(__always) func apply(block: (Self) -> ()) -> Self {
        block(self)
        return self
    }
    @inline(__always) func with<R>(block: (Self) -> R) -> R {
        return block(self)
    }
}

This protocol and extension provides two inline functions, where one can be served to return processed object, and the other is strictly similar to with in Kotlin and other languages (Visual Basic supported in 90s).

Usage

Specify types which these functions should apply to:

extension NSObject: ScopeFunc {} 

apply:

let imageView = UIImageView().apply {
    $0.contentMode = .scaleAspectFit
    $0.isOpaque = true
}

Here we create an object and once the closure is executed, modified object is returned.

with:

imageView.with {
    $0.isHidden = true
}

Works equal to with in Kotlin.

Originaly based on this source code.

NOTE:

Swift compiler is generally regarded as smart enough to decide whether or not a function should be inlined. Quite likely, these two would be inlined due to their relative compactness even without strictly specifying @inline (__always). Either way, you should know that this keyword does not affect the logic and the result of these, because inlining is about optimizing the program.

Upvotes: 15

Related Questions