MVVM-C Architecture

--

It enables to write modular, reactive programming, clean, maintainable, and testable code.

Understanding MVVM

MVVM -C is composed of four main components:

Model:

  • The Model represents the data and business logic of the application.
  • It includes data structures, databases, network calls, and other components responsible for data management.
  • The Model is independent of the user interface and the ViewModel.

View:

  • This is the user interface layer where the user interacts with the app.
  • It includes UI components such as buttons, labels, text fields, and views.
  • The View is responsible for displaying data and capturing user input.

ViewModel:

  • The ViewModel acts as an intermediary between the View and the Model.
  • It holds the presentation logic and business logic related to the View.
  • The ViewModel prepares and provides data to the View for display.
  • It also handles user input and communicates with the Model for data manipulation.
  • ViewModels can be reused across multiple Views since they are independent of UI components.

App Coordinator:

Manages the overall flow of the application.

Below you can find the Big Scale Blueprint and Small Scale Blueprint of MVVM Architecture.

Example with Big Scale

Pros:

  1. Mvvm makes the view controller simpler by moving a lot of business logic out of it.
  2. The view model better expresses the business logic for the view.
  3. View model is much easier to test then a view controller.

Con’s:

  1. For Beginners, MVVM will be hard to implement.
  2. Apps with simple UI, MVVM can be overkill.
  3. For larger apps, data binding will complex, So debugging will be hard

Create API manager

enum ErrorType: Error {
case decodeFailed
}

enum HTTPSMethod: String {
case GET = "GET"
case POST = "POST"
}

protocol USERRequest {
associatedtype Response: Decodable
var url: URL {get}
var httpsMethod: HTTPSMethod { get set }
}

protocol APIManagerProtocol {
func getData<T: USERRequest>(request: T, completion:@escaping(Result<T.Response, ErrorType>)->Void)
}

class APIManager: APIManagerProtocol {
private let https: HTTPSClientProtocol?
private let decodeData: DecodeDataProtocol?

init(https: HTTPSClientProtocol?, decodeData: DecodeDataProtocol?) {
self.https = https
self.decodeData = decodeData
}

func getData<T: USERRequest>(request: T, completion:@escaping(Result<T.Response, ErrorType>)->Void) {
https?.getDataFromServer(url: request.url, methodType: request.httpsMethod, completion: { [weak self] result in
guard let self = self else { return }
switch result {

case .success(let data):

self.decodeData?.decode(data: data, completionHandler: { (decodeData: Result<T.Response, ErrorType>) in

switch decodeData {
case .success(let response):
completion(.success(response))
case .failure(let error):
completion(.failure(.decodeFailed))
}

})

case .failure(let error):
completion(.failure(error))
}
})
}
}

protocol HTTPSClientProtocol {
func getDataFromServer(url: URL, methodType: HTTPSMethod, completion:(Result<Data, ErrorType>)->Void)
}

class HTTPSClient: HTTPSClientProtocol {
func getDataFromServer(url: URL, methodType: HTTPSMethod, completion:(Result<Data, ErrorType>)->Void) {

// Mock API Call
let mockData = """
{ "message": "Hello, World!" }
""".data(using: .utf8)

if let data = mockData {
completion(.success(data))
} else {
completion(.failure(.decodeFailed))
}

}
}

protocol DecodeDataProtocol {
func decode<T: Decodable>(data: Data, completionHandler: @escaping (Result<T, ErrorType>)->Void)
}

class DecodeData: DecodeDataProtocol {
func decode<T: Decodable>(data: Data, completionHandler:@escaping (Result<T, ErrorType>)->Void) {

do {
let decodedData = try JSONDecoder().decode(T.self, from: data)
completionHandler(.success(decodedData))
} catch {

}
}
}

struct SampleResponse: Decodable {
let message: String
}

struct SampleRequest: USERRequest {
typealias Response = SampleResponse

var url: URL = URL(string: "hghjg")!

var httpsMethod: HTTPSMethod = .GET
}

var apiManger2 = APIManager(https: HTTPSClient(), decodeData: DecodeData())

apiManger2.getData(request: SampleRequest()) { result in
switch result {
case .success(let response):
print("Success: \(response.message)")
case .failure(let error):
print("Error: \(error)")
}
}

--

--

No responses yet