3

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?

3
  • 1
    It's really not Apple who doesn't like C loops, but Erica Sadun, Andy Matuschak (Khan Academy), Keith Smiley (Lyft), and others in the mailing list. Chris Lattner (Apple) is just open to them. Finally the committee agreed to remove it. Commented Mar 25, 2016 at 3:20
  • Please don't add the solution as "Update" to the question. If you want to share your own solution, you can post an answer. Commented Nov 28, 2016 at 9:28
  • 1
    @FelixSFD thanks for advice, extracted that to answer Commented Nov 28, 2016 at 9:36

3 Answers 3

2

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
}
Sign up to request clarification or add additional context in comments.

11 Comments

Optional can be safely unwrapped inside the while loop
I updated my answer to use an implicit optional instead of an explicit one.
This will crash when parentView becomes nil finally. You can leave it as it was before, or unwrap the optional inside the while loop
It doesn't crash on the IBM Swift Sandbox 3.0-dev. The code I tested is here.
not nice :/ we declare variable outside of the loop, make iteration in the end of code block, and at all have too many lines of code
|
1

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
})

1 Comment

You do not need the for in if you are not using it. Use only the sequence function.
0

For instance

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.