मैं स्विफ्ट 4 में लिखे एक आईओएस एप्लिकेशन को देख रहा हूं। इसमें URLSession का उपयोग करते हुए एक काफी सरल नेटवर्क परत है, हालांकि एप्लिकेशन में कोई यूनिट परीक्षण नहीं है और इससे पहले कि मैं एक रिफैक्टर शुरू करूं, मैं कई परीक्षण शुरू करके इसे संबोधित करने के लिए उत्सुक हूं।

इससे पहले कि मैं ऐसा कर सकूं, मुझे URLSession का मजाक उड़ाने में सक्षम होना चाहिए ताकि मैं परीक्षण के दौरान वास्तविक नेटवर्क अनुरोध न बना सकूं। मैं वर्तमान कार्यान्वयन में नहीं देख सकता कि मैं इसे कैसे प्राप्त कर सकता हूं? मेरे परीक्षणों में URLSession इंजेक्ट करने के लिए मेरे लिए एक प्रवेश बिंदु कहां है।

मैंने नेटवर्क कोड निकाला है और उसी तर्क का उपयोग करके एक साधारण ऐप बनाया है, जो इस तरह दिखता है:

समापन बिंदु.स्विफ्ट

import Foundation

protocol Endpoint {
    var baseURL: String { get }
}

extension Endpoint {
    var urlComponent: URLComponents {
        let component = URLComponents(string: baseURL)
        return component!
    }

    var request: URLRequest {
        return URLRequest(url: urlComponent.url!)
    }
}

struct RandomUserEndpoint: Endpoint {
    var baseURL: String {
        return RandomUserClient.baseURL
    }
}

APIClient.swift

import Foundation

enum Either<T> {
    case success(T), error(Error)
}

enum APIError: Error {
    case unknown, badResponse, jsonDecoder
}

enum HTTPMethod: String {
    case get = "GET"
    case put = "PUT"
    case post = "POST"
    case patch = "PATCH"
    case delete = "DELETE"
    case head = "HEAD"
    case options = "OPTIONS"
}

protocol APIClient {
    var session: URLSession { get }
    func get<T: Codable>(with request: URLRequest, completion: @escaping (Either<T>) -> Void)
}

extension APIClient {
    var session: URLSession {
        return URLSession.shared
    }

   func get<T: Codable>(with request: URLRequest, completion: @escaping (Either<T>) -> Void) {
        let task = session.dataTask(with: request) { (data, response, error) in
            guard error == nil else { return completion(.error(error!)) }
            guard let response = response as? HTTPURLResponse, 200..<300 ~= response.statusCode else { completion(.error(APIError.badResponse)); return }

            guard let data = data else { return }

            guard let value = try? JSONDecoder().decode(T.self, from: data) else { completion(.error(APIError.jsonDecoder)); return }

            DispatchQueue.main.async {
                completion(.success(value))
            }
        }
        task.resume()
    }

}

RandomUserClient.swift

import Foundation

class RandomUserClient: APIClient {
    static let baseURL = "https://randomuser.me/api/"

    func fetchRandomUser(with endpoint: RandomUserEndpoint, method: HTTPMethod, completion: @escaping (Either<RandomUserResponse>)-> Void) {
        var request = endpoint.request
        request.httpMethod = method.rawValue
        get(with: request, completion: completion)
    }

}

RandomUserModel.swift

import Foundation

typealias RandomUser = Result

struct RandomUserResponse: Codable {
    let results: [Result]?
}

struct Result: Codable {
    let name: Name
}

struct Name: Codable {
    let title: String
    let first: String
    let last: String
}

इस कोड का उपभोग करने के लिए एक बहुत ही सरल ऐप कुछ इस तरह हो सकता है

class ViewController: UIViewController {

    let fetchUserButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("FETCH", for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 36)
        button.addTarget(self, action: #selector(fetchRandomUser), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.isEnabled = true
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        view.addSubview(fetchUserButton)
        NSLayoutConstraint.activate([
            fetchUserButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            fetchUserButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
    }

    @objc func fetchRandomUser() {
        let client = RandomUserClient()
        fetchUserButton.isEnabled = false
        client.fetchRandomUser(with: RandomUserEndpoint(), method: .get) { [unowned self] (either) in
            switch either {
            case .success(let user):
                guard let name = user.results?.first?.name else { return }
                let message = "Your new name is... \n\(name.first.uppercased()) \(name.last.uppercased())"
                self.showAlertView(title: "", message: message)
                self.fetchUserButton.isEnabled = true
            case .error(let error):
                print(error.localizedDescription)
            }
        }
    }

    func showAlertView(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
        alert.addAction(UIAlertAction(title: "Close", style: UIAlertAction.Style.default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}

आदर्श रूप से मुझे URLSession का मज़ाक उड़ाने का एक तरीका चाहिए ताकि मैं इसका सही परीक्षण कर सकूं, हालांकि मुझे यकीन नहीं है कि मैं इसे वर्तमान कोड के साथ कैसे प्राप्त कर सकता हूं।

5
Harry Blue 2 अक्टूबर 2018, 16:13

1 उत्तर

सबसे बढ़िया उत्तर

इस मामले में यह वास्तव में अधिक समझ में आता है यदि आप RandomUserClient के आसपास जोर देते हैं।

आप RandomUserClient का विस्तार करते हैं और इसे URLSession का एक उदाहरण स्वीकार करते हैं, जिसे स्वयं आपके APIClient में इंजेक्ट किया जाता है।

class RandomUserClient: APIClient {
    var session: URLSession
    static let baseURL = "https://randomuser.me/api/"

    init(session: URLSession) {
        self.session = session
    }

    func fetchRandomUser(with endpoint: RandomUserEndpoint, method: HTTPMethod, completion: @escaping (Either<RandomUserResponse>)-> Void) {
        var request = endpoint.request
        request.httpMethod = method.rawValue

        get(with: request, session: session, completion: completion)
    }

}

आपके व्यू कंट्रोलर को अपडेट करने की आवश्यकता होगी ताकि RandomUserClient को lazy var client = RandomUserClient(session: URLSession.shared) जैसा कुछ इनिशियलाइज़ किया जा सके

URLSession की नई अंतःक्षेपित निर्भरता को स्वीकार करने के लिए आपके APIClient प्रोटोकॉल और एक्सटेंशन को भी पुन: सक्रिय करने की आवश्यकता होगी

protocol APIClient {
    func get<T: Codable>(with request: URLRequest, session: URLSession, completion: @escaping (Either<T>) -> Void)
}

extension APIClient {
    func get<T: Codable>(with request: URLRequest, session: URLSession, completion: @escaping (Either<T>) -> Void) {
        let task = session.dataTask(with: request) { (data, response, error) in
            guard error == nil else { return completion(.error(error!)) }
            guard let response = response as? HTTPURLResponse, 200..<300 ~= response.statusCode else { completion(.error(APIError.badResponse)); return }

            guard let data = data else { return }

            guard let value = try? JSONDecoder().decode(T.self, from: data) else { completion(.error(APIError.jsonDecoder)); return }

            DispatchQueue.main.async {
                completion(.success(value))
            }
        }
        task.resume()
    }

}

session: URLSession को जोड़ने पर ध्यान दें।

4
nodediggity 2 अक्टूबर 2018, 14:06