Function assigning values after it returns Swift

2019-07-26 09:33发布

问题:

I'm running into a weird bug where my function appends a value to an array AFTER it returns... The code for this is below :

func makeUser(first: String, last: String, email: String) -> [User] {

    var userReturn = [User]()

    RESTEngine.sharedEngine.registerUser(email, firstName: first, lastName: last, age: 12, success: { response in
        if let response = response, result = response["resource"], id = result[0]["_id"] {

            let params: JSON =
            ["name": "\(first) \(last)",
             "id": id as! String,
             "email": email,
             "rating": 0.0,
             "nuMatches": 0,
             "nuItemsSold": 0,
             "nuItemsBought": 0]
             let user = User(json: params)

            userReturn.append(user)
            print("\(userReturn)")

        }
        }, failure: { error in
            print ("Error creating a user on the server: \(error)")
    })

    return userReturn
}

I call make user from here:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    var newUser = makeUser("Average", last: "Person", email: "a.Person@mail.com")
    print("\(newUser)")
}

(This is all still testing so I'm obviously calling my code in weird places.)

So when I run this what ends up happening is that FIRST my "newUser" array gets printed (and it shows up empty), and afterwards the userReturn array that I assign locally within the makeUser function prints, and it contains the new user that I append to it within the "success" completion block of "registerUser", like so:

Does anyone know whats happening here, and how I could fix it?

For reference: JSON is simply a typealias I defined for [String: AnyObject] dictionary.

回答1:

The registerUser runs asynchronously, so you should apply asynchronous pattern, such as completion handler:

func makeUser(first: String, last: String, email: String, completionHandler: ([User]?, ErrorType?) -> ()) {
    RESTEngine.sharedEngine.registerUser(email, firstName: first, lastName: last, age: 12, success: { response in
        if let response = response, result = response["resource"], id = result[0]["_id"] {
            var users = [User]()

            let params: JSON =
            ["name": "\(first) \(last)",
             "id": id as! String,
             "email": email,
             "rating": 0.0,
             "nuMatches": 0,
             "nuItemsSold": 0,
             "nuItemsBought": 0]
            let user = User(json: params)
            users.append(user)

            completionHandler(users, nil)
        } else {
            let jsonError = ...  // build your own ErrorType or NSError indicating that the the parsing of the JSON failed for some reason
            completionHandler(nil, jsonError)
        }
    }, failure: { error in
        completionHandler(nil, error)
    })
}

And use it like so:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    makeUser("Average", last: "Person", email: "a.Person@mail.com") { users, error in
        guard error == nil else {
            print(error)
            return
        }

        print("\(users)")
        // if you're doing anything with this, use it here, e.g. reloadTable or update UI controls
    }

    // but don't try to use `users` here, as the above runs asynchronously
}