I make a generic function using Alamofire
This is function
class APIServices {
private init() {}
static let instance = APIServices()
func getData<T: Decodable, E: Decodable>(url: String, method: HTTPMethod ,params: Parameters?, encoding: ParameterEncoding ,headers: HTTPHeaders? ,completion: @escaping (T?, E?, Error?)->()) {
AF.request(url, method: method, parameters: params, encoding: encoding, headers: headers)
.validate(statusCode: 200...300)
.responseJSON { (response) in
switch response.result {
case .success(_):
guard let data = response.data else { return }
do {
let jsonData = try JSONDecoder().decode(T.self, from: data)
completion(jsonData, nil, nil)
} catch let jsonError {
print(jsonError)
}
case .failure(let error):
// switch on Error Status Code
guard let data = response.data else { return }
guard let statusCode = response.response?.statusCode else { return }
switch statusCode {
case 400..<500:
do {
let jsonError = try JSONDecoder().decode(E.self, from: data)
completion(nil, jsonError, nil)
} catch let jsonError {
print(jsonError)
}
default:
completion(nil, nil, error)
}
}
}
}
}
and use it in HomeViewModel
class HomeViewModel {
var loadingBehavior = BehaviorRelay<Bool>(value: false)
private var homeModelSubject = PublishSubject<[Book]>()
private var isTableHidden = BehaviorRelay<Bool>(value: false)
var homeModelObservable: Observable<[Book]> {
return homeModelSubject
}
var isTableHiddenObservable:Observable<Bool> {
return isTableHidden.asObservable()
}
func getBooks(handler: @escaping networkHandler) {
loadingBehavior.accept(true)
let url = "https://simple-books-api.glitch.me/books"
APIServices.instance.getData(url: url, method: .get, params: nil, encoding: JSONEncoding.default, headers: nil) {[weak self] (bookModel: [Book]?, baseError: HomeBaseError?, error) in
guard let self = self else { return }
self.loadingBehavior.accept(false)
if let error = error {
print("this is error discription\(error.localizedDescription)")
} else if let baseError = baseError {
print(baseError.error)
} else {
guard let bookModel = bookModel else { return }
if bookModel.count > 0 {
self.homeModelSubject.onNext(bookModel)
self.isTableHidden.accept(false)
} else {
self.isTableHidden.accept(true)
}
}
}
}
}
the problem is when close wifi loading do not stop and do not print error.localizedDescription and when I make debug do not go to if let error ...
It's hard to understand exactly what goes wrong but I would suggest removing the obvious bugs you have in (at least?) 4 places where you do not use your completion handler.
The first one is
guard let data = response.data else { return }
This should be changed to
guard let data = response.data else {
completion(nil, nil, nil)
return
}
or if you consider this to be an error you might want something like
guard let data = response.data else {
completion(nil, nil, NoDataError())
return
}
where NoDataError is a custom Error you need to create. You need to fix this in two places.
The other situation is when you get a decoding error (also in two places)
} catch let jsonError {
print(jsonError)
}
Here you should also use your completion handler
} catch let jsonError {
print(jsonError)
completion(nil, nil, jsonError)
}
You also have a logical bug when receiving an error (failure) because you are checking the http status code before decoding but you will never get a response if you have a status code > 200
So when you get a .failure
you should only decode the error, the whole response status logic you have needs to be separated from your success/failure logic