Search code examples
swiftif-statementnested-loops

Resolving too many else-if statements in Swift 3


Problem: Given the inputs of the function, test each user to make sure they conform to the following conditions: 1. Each user in the users array cannot share a chatroom with the current user. (The Chatroom object has two properties 'firstUserId', and 'secondUserId'.
2. Each user in the users array is not the current user. 3. Each user in the users array is within 5 mile radius of current user.

At the call sight of the completion handler, I check if a User object has a value of true, if so, I show it to the current user as a potential match.

Now, I quickly brute forced this solution, but cringe every time I look at it. It just seems very inefficient. Any tips on a more elegant solution is much appreciated!

typealias validUsersCompletionHandler = (_ users: [User: Bool]) -> Void

private func validateNewUsers(currentUser: User, users: [User], chatrooms: [Chatroom], completionHandler: validUsersCompletionHandler?) {

    var results: [User: Bool] = [:]

    let currentUserCoords = CLLocation(latitude: currentUser.latitude, longitude: currentUser.longitude)

    for user in users {
        let newUserCoords = CLLocation(latitude: user.latitude, longitude: user.longitude)
        let distance = currentUserCoords.distance(from: newUserCoords)
        // // 1 mile = 1609 meters, 8046.72 = 5 miles.
        for chatroom in chatrooms {
            if currentUser.id == chatroom.firstUserId && user.id == chatroom.secondUserId {
                results[user] = false
            } else if currentUser.id == chatroom.secondUserId && user.id == chatroom.firstUserId {
                results[user] =  false
            } else if user.id == currentUser.id {
                results[user] = false
            } else if distance > 8046.72 {
                results[user] = false
            } else {
                results[user] = true
            }
        }
    }
    completionHandler?(results)
}

// ************************************************************************

// Below is my revised version of the method. Slightly more elegant I suppose?

// ************************************************************************

typealias validUsersCompletionHandler = (_ users: [User: Bool]) -> Void

private func validateNewUsers(currentUser: User, users: [User], chatrooms: [Chatroom], completionHandler: validUsersCompletionHandler?) {

    var results: [User: Bool] = [:]

    var isInRange = false

    var distance: Double = 0 {
        didSet {
            if distance > 8046.72 {
                isInRange = false
            } else {
                isInRange = true
            }
        }
    }

    let currentUserCoords = CLLocation(latitude: currentUser.latitude, longitude: currentUser.longitude)

    let currentUserId = currentUser.id

    for user in users {

        let userId = user.id

        let newUserCoords = CLLocation(latitude: user.latitude, longitude: user.longitude)

        distance = currentUserCoords.distance(from: newUserCoords)
        // // 1 mile = 1609 meters, 8046.72 = 5 miles.

        for chatroom in chatrooms {

            switch (currentUserId, userId, isInRange) {

            case (chatroom.firstUserId,chatroom.secondUserId, _), (_, _, false),(chatroom.secondUserId, chatroom.firstUserId, _), (_, currentUserId, _): results[user] = false

            default: results[user] = true

            }
        }

    }

    completionHandler?(results)

}

}


Solution

  • You can replace the if statement with switch... Or you can make tuple with (currentUserId, userId)

     //always check for optionals
        guard let currentUserId = currentUser.id, let userId = user.id, else{
        return
        }
       //The switch should have this format:
        switch (currentUserId, userId){
        //currentUserId == chatroom.firstUserId,  userId = chatroom.secondUserId)
        case (chatroom.firstUserId,chatroom.secondUserId):
        //do your things
        break
        case (chatroom.secondUserId,firstUserId):
        //do other things
        break
        default:
        break
        }
    

    You can even use the case with declaration or comparing more options:

        switch value{
        case let x where value > 10:
        //When value is bigger than 10..etc
        default:
        break
        }
    

    For better use, see: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html

    Wish happy coding :)