brigadir
brigadir

Reputation: 6942

Swift 3: how to write this for(;;) loop

Looks like Apple doesn't like C loops, but doesn't provide good approach over it (or I couldn't find it). I have such loop to go from some view to the root in UI hierarchy:

for var parentView = view; parentView != nil; parentView = parentView.parent {
    ...
}

How to write this in Swift 3 manner?

Upvotes: 3

Views: 498

Answers (3)

brigadir
brigadir

Reputation: 6942

Correct but too-late answer: there are built-in functions in Swift 3 which provide the solution:

public func sequence<T>(first: T, next: (T) -> T?) -> UnfoldSequence<T, (T?, Bool)>
public func sequence<T, State>(state: State, next: (inout State) -> T?) -> UnfoldSequence<T, State>

We can use them in this manner:

sequence(first: view, next: {
    // do something with $0...
    return $0.superview
})

Upvotes: 1

Yakiv Kovalskyi
Yakiv Kovalskyi

Reputation: 1757

For instance

for view in views where view.superview != nil {
}

Upvotes: 0

Valentin
Valentin

Reputation: 11802

This would be a way to do it in Swift 3:

var parentView: View! = view
while parentView != nil {
    // Do stuff
    parentView = parentView.parent
}

If you want to group the loop progression stuff next to while and not at the end of block, you may use defer, like this:

var parentView: View! = view
while parentView != nil {
    defer { parentView = parentView.parent }        
    // Do stuff
}

If you want to limit the scope of parentView, you can encapsulate everything in a do block:

do {
    var parentView: View! = view
    while parentView != nil {
        defer { parentView = parentView.parent }        
        // Do stuff
    }
}

But it's quite verbose so you could define a new generic function for similar loops, like this:

func kindaCStyleLoop<T>(first: T, obtainNext: T -> T?, action: T -> ()) {
    var current: T! = first
    repeat {
        action(current)
        current = obtainNext(current)
    } while current != nil
}

kindaCStyleLoop(view, obtainNext: { $0.parent }) {
    // Do stuff with $0
}

And a last one that relies on GeneratorType and SequenceType to enable using the for-in-loop syntax:

struct CStyleGenerator<T> : GeneratorType, SequenceType {
    let getNext: T -> T?
    var current: T!

    init(first: T, getNext: T -> T?) {
        self.getNext = getNext
        self.current = first
    }

    mutating func next() -> T? {
        defer {
            if current != nil {
                current = getNext(current)
            }
        }
        return current
    }
}

for parentView in CStyleGenerator(first: view, getNext: { $0.parent }) {
    // Do stuff with parentView
}

Upvotes: 2

Related Questions