campana
campana

Reputation: 167

Swift 3 Access dictionary (json) values from enum

I'm trying to use an enum as described in this post https://8thlight.com/blog/mike-knepper/2017/01/30/keeping-third-party-swift-dependencies-testable.html and am having a bit of trouble understanding how to use the enum return as my response. I've setup my code for a successful test (removed the fail and error cases for now) as:

the enum response return definitions:

public struct Constants {
    public typealias jsonData = [String: Any]
    public typealias jsonErrors = [[String: Any]]
}
public enum ServiceResponse {
    case success(Constants.jsonData)
    case errors(Constants.jsonErrors)
    case failure(String)
}

My mock response as:

private let successData: Constants.jsonData = [
    "user_email": "[email protected]",
    "user_display_name": "tester smith",
    "user_avatar_url": "",
    "user_id": 1
]

private let successfulResponse = ServiceResponse.success(successData)

And the mock adapter (used for my test) as:

class MockSuccessfulNetworkAdapter: NetworkAdapter {
    func post(destination: String, payload: [String: Any], responseHandler: @escaping (ServiceResponse) -> ()) {
        responseHandler(successfulResponse)
    }
}

My service being tested is:

    public func validateUser(_ userData: [String: Any]?, completed: ((_ response: ServiceResponse) -> ())? ) {

        let loginEndpoint = "\(Constants.BASE_ENDPOINT)\(Constants.LOGIN_ENDPOINT)"
        if let userPayload = userData {

            networkAdapter.post(destination: loginEndpoint, payload: userPayload, responseHandler: { (response) -> Void in
                print("User validate call response was \(response)")               
                completed?(response)
            })
        }
        else {
            print( "Invalid User attempt to write JSON")
            return
        }
    }

And the test (with some of the avenues I've tried):

func testValidateAValidUser() {
    let networkAdapter = MockSuccessfulNetworkAdapter()

    if  let loginService = LoginService(Constants.BASE_ENDPOINT, networkAdapter) {
        let userData = [
            "email": "[email protected]",
            "password": "password"
        ]

        loginService.validateUser(userData, completed: { (response: ServiceResponse?) -> Void in
            print(response)

                // case ServiceResponse.success (let data) {

                    print("Response for validate user -> \(response!)")
//                    var data = response? as! Constants.jsonData
//                    if (response == ServiceResponse.success(data)) {
//                        // let em = response ["email"]
//                    }
                // XCTAssertTrue(response == ServiceResponse.success)
                // if let em = response ["email"] {
                    print("OK!!")
                // }
                // XCTAssertTrue( , "[email protected]")
            // }
            })
        }
    }    

I like the idea of utilizing the enums and the dependency injection for my testing, but can't seem to wrangle getting the actual values (as I will have to when I move to the alamofire or something to decode the actual values. Any help greatly appreciated!

Edit: add response

What I'm seeing as my response is correct when running the test: enter image description here

Upvotes: 1

Views: 320

Answers (1)

jlmurph
jlmurph

Reputation: 1090

So you're almost there, the simplest way to get the enum case value like what you're doing is by using a switch statement, case-ing all the cases (lol), and assigning a let constant in the parameter. Your validateUser method inside your testValidate function would look like this:

loginService.validateUser(userData, completed: { (response: ServiceResponse?) -> Void in
        print(response)

        guard let resp = response else {
            return
        }

        switch resp {
        case .errors(let error) :
            print(error)
        case .failure(let failed) :
            print(failed)
        case .success(let json) :
            print(json)
            //execute your json..
        }
    })

I found an interesting article too that touches on such a use case: https://medium.com/@jbergen/you-ve-been-using-enums-in-swift-all-wrong-b8156df64087.

Also, since you've got an error and failed response case.. i'd go with removing the optional on response reply variable, you could just provide the proper ServiceResponse case depending on your network task; thus skipping the need for an unwrap. food for thought!

Upvotes: 1

Related Questions