# Protocol and Mock webService

In this example, an application uses data from an API.

For the creation of the UI, we don't want to make a call at each simulation on device.

Let's suppose that the back is not ready, or that we want to test a particular error case. 

So we will simulate the call with a function that will return data to display.

We will define a protocol, and a class that conforms to the protocol. This class will be implemented to return predefined data. 
Then when the interface is complete, we will implement a class that will make a network call.

Our example will be very simple, with just a text (a joke) displayed in a SwiftUI view. 
Let's code.

## The Model
We will get a random joke from the API `api.chucknorris.io`.
For this simple example, we just use the text of the joke (key `value`).

```Swift
struct Joke: Codable {

    let value: String

}
```

## Create a Protocol

The protocol includes a function that returns the joke.

```Swift
protocol JokeProtocol {
    func getJoke(_ completion: @escaping (Result<Joke, Error>) -> Void)
}
```

## Mock Service
We add a class that conforms to the protocol.

```Swift
class MockJokeService: JokeProtocol {
	func getJoke(_ completion: @escaping (Result<Joke, Error>) -> Void) {
      code
    }
}
```

And let's implement the function

```swift
class MockJokeService: JokeProtocol {
	func getJoke(_ completion: @escaping (Result<Joke, Error>) -> Void) {
	
      let response = Joke(value: "This test is not a joke!"
      completion(.success(response))
    }
}
```


## ViewModel
We create a viewModel to process the value outside the view.
With real network calls, at the touch of a button, a new joke appears. We use a property with the *Published* attribute and the *ObservableObject* type, so that each new joke is displayed.

```Swift
class JokeViewModel: ObservableObject {

	let service: JokeProtocol

    init(service: JokeProtocol) {
		self.service = service
    }

    @Published var joke = ""

    func fetchData() {

        service.getJoke { [weak self] result in
            DispatchQueue.main.async {
				switch result {
				case let .success(jokeResponse):
					self?.joke = jokeResponse.value
				case .failure: 
					break
                }
            }
        }
    }
}
```

## The view
```Swift
import SwiftUI

struct ContentView: View {

    @ObservedObject var viewModel: JokeViewModel

    var body: some View {

        NavigationStack {
            VStack 
				Spacer()
                Text(viewModel.joke)
                    .font(.title3)
                Spacer()
                Button("Show an other joke") {
                    viewModel.fetchData()
                }
                .buttonStyle(.bordered)
            }
            .padding()
            .navigationTitle("C. Norris Jokes")
        }
        .onAppear() {
            viewModel.fetchData()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(viewModel: JokeViewModel(service: MockJokeService()))
    }
}
```

For this example we specify the service for the ContentView in the scene.

```Swift
@main
struct MockWebServiceApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: JokeViewModel(service: MockJokeService()))
        }
    }
}
```

## Result

When we launch the application, we get a window with a button at the bottom and a text in the center that has been entered in the MockJokeService : 

`This test is not a joke!`

## Test
We can test the *getJoke()* method call by adding a counter.

```Swift
class MockJokeService: JokeProtocol 

	var callCount = 0
	
	func getJoke(_ completion: @escaping (Result<Joke, Error>) -> Void) {

		callCount += 1
		let response = Joke(value: "This test is not a joke!
		completion(.success(response))
    }
}
```

The unit test is :
```Swift
import XCTest
@testable import MockWebService

final class MockWebServiceTests: XCTestCase {

    func testJokeViewModel() {
        let mockedService = MockJokeService()
        let viewModel = JokeViewModel(service: mockedService)
        viewModel.fetchData()
        XCTAssertEqual(mockedService.callCount, 1)
    }
}
```

## WebService: real call
We add a class that conforms to the protocol and implement the real call :

```Swift
class JokeService: JokeProtocol {

    func getJoke(_ completion: @escaping (Result<Joke, Error>) -> Void) {

        let url = URL(string: "https://api.chucknorris.io/jokes/random")!

        URLSession.shared.dataTask(with: url) { data, _, error in
            guard error == nil else {
                completion(.failure(error!))
                return
            }
            let decoded = try! JSONDecoder().decode(Joke.self, from: data!)
            completion(.success(decoded))
        }.resume()
    }
}
```

To get the jokes from the API, we change the service of the ContentView in the scene :

```Swift
@main
struct MockWebServiceApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: JokeViewModel(service: JokeService()))
        }
    }
}
```

## Service injection

To avoid having to swap the service in the scene, we can create a configuration enum, a service property and assign it the appropriate service according to the chosen scheme :

```Swift
import SwiftUI

enum SchemeConfig {
    case webService
    case mock
}

#if MOCK
let schemeConfig: SchemeConfig = .mock
#else
let schemeConfig: SchemeConfig = .webService
#endif

@main
struct MockWebServiceApp: App {

    let service: JokeProtocol

    init() {
        switch schemeConfig {
        case .webService:
	        self.service = JokeService()
        case .mock:
            self.service = MockJokeService()
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: JokeViewModel(service: service))
        }
    }
}
```

## Schemes configuration

The project includes a schema for the webService, so we add one for the Mock.

In the *PROJECT* tab **info**, select **Debug** and click on **+**.

Click on **Duplicate Debug Configuration**.

Rename **Debug copy** to **Debug Mock**.

Edit Scheme, press **Duplicate Scheme**, and name the new scheme **ProjectName Mock**.

There is now 1 scheme ProjectName and 1 scheme ProjectName Mock.


![Scheme.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1666034174590/uaePcaL_7.png align="left")

Then choose the scheme **ProjectName Mock** and **edit** it.

For **Run**, go to the *info* tab

Fill in the *Build Configuration* with **Debug Mock**, same for *Test* and *Analysis*.

Finally in **Target ProjectName** tab **Build Settings**, selecting *All* and *Combined*.

In the **Swift Compiler - Custom Flags** section

Change the value of the Setting *Debub Mock* by *DEBUG MOCK*.
Attention to capitalization.


![Flags.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1666034194716/EmugH17ZA.png align="left")

Now you just have to choose the scheme to run the Mock or webService.

## Finally

Here the example simply uses a String as a mock. 

But the mock can be an array of String. 
The view button to display the strings index by index.

The mocked data can be displayed in a list, or other.

Thanks for your attention, and enjoy code.
