fetching different data from one function - ios

I am fetching data for different json models from multiple functions:
func fetchMovies(from moviesUrl: String, _ completion: #escaping (NowPlayingResponse?) -> Void){
guard let safeUrl = URL(string: moviesUrl + apiKey) else {return}
let alamofireRequest = AF.request(safeUrl)
alamofireRequest.responseDecodable(of: NowPlayingResponse.self, decoder: jsonDecoder){ response in
switch response.result {
case .success:
guard let safeMovies = response.value else {
completion(nil)
return
}
completion(safeMovies)
case .failure(let error):
print(error)
completion(nil)
}
}
}
public func fetchGenres(from genresUrl: String, _ completion: #escaping (GenresResponse?) -> Void) {
guard let safeUrl = URL(string: genresUrl + apiKey) else {return}
let alamofireRequest = AF.request(safeUrl)
alamofireRequest.responseDecodable(of: GenresResponse.self, decoder: jsonDecoder){ response in
switch response.result {
case .success:
guard let safeGenres = response.value else {
completion(nil)
return
}
completion(safeGenres)
case .failure(let error):
print(error)
completion(nil)
}
}
}
I tried to make one function which will handle all types so it made my code cleaner:
func fetchMovies<T: Codable>(from moviesUrl: String, _ completion: #escaping (T?) -> Void){
guard let safeUrl = URL(string: moviesUrl + apiKey) else {return}
let alamofireRequest = AF.request(safeUrl)
alamofireRequest.responseDecodable(of: T.self, decoder: jsonDecoder){ response in
switch response.result {
case .success:
guard let safeMovies = response.value else {
completion(nil)
return
}
completion(safeMovies)
case .failure(let error):
print(error)
completion(nil)
}
}
}
When I called function to fetch data:
func getMoviees(){
MovieServiceAPI.shared.fetchMovies(from: "https://api.themoviedb.org/3/movie/now_playing") { (moviesResponse) in
guard let safeMovies = moviesResponse?.results else {return}
self.movieList = safeMovies
DispatchQueue.main.async { [unowned self] in
hideLoader()
self.tableView.reloadData()
}
}
}
I am getting Generic parameter 'T' could not be inferred error. I tried to put MovieServiceAPI.shared.fetchMovies<NowPlayingReponse: Codable> where NowPlayingResponse represents json model for data. Where am I getting wrong?

Related

JSON Decoding Not Populating Table View

I am trying to parse data from the website movieDatabase.com However there's some issue decoding the data to json and populating my table view.I am not sure why this is happening. Please I need help spotting out the problem. Here's my code. https://github.com/lexypaul13/Movie-Browser/tree/main/Movie-Browser
struct Movies: Codable {
let overview:String?
let original_title: String?
let poster_path:String
}
struct ApiResponse:Codable, Hashable {
let page:Int
let shows:[Movies]
enum CodingKeys:String, CodingKey {
case page = "page"
case shows = "results"
}
}
class NetworkManger{
enum EndPoint{
case showList
}
static let shared = NetworkManger()
private let baseURL : String
private var apiKeyPathCompononent :String
private init(){
self.baseURL = "https://api.themoviedb.org/3/movie/now_playing?"
self.apiKeyPathCompononent = "api_key=a07e22bc18f5cb106bfe4cc1f83ad8ed"
}
private var jsonDecoder:JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}()
func get<T:Decodable>(_ endPoints: EndPoint, urlString: String, completed:#escaping(Result<T?,ErroMessage>)->Void){
guard let url = urlBuilder(endPoint: endPoints) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url){ data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode==200 else {
print(ErroMessage.invalidResponse.rawValue)
completed(.failure(.invalidResponse))
return
}
guard let data = data else{
completed(.failure(.invalidData))
return
}
do{
let apiResponse = try self.jsonDecoder.decode([T].self, from: data)
DispatchQueue.main.async {
completed(.success(apiResponse as? T))
}
} catch{
print(ErroMessage.invalidData.rawValue)
}
}
task.resume()
}
private func urlBuilder(endPoint:EndPoint )->URL?{
switch endPoint {
case .showList:
return URL(string: baseURL + apiKeyPathCompononent )
}
}
func getMovies(){
NetworkManger.shared.get(.showList, urlString: "") { [weak self] (result: Result<[Movies]?,ErroMessage> ) in
guard let self = self else { return }
switch result{
case .success(let movies):
self.movies = movies ?? []
DispatchQueue.main.async {self.tableView.reloadData()}
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
The root object returned from the api is your ApiResult struct. This contains an array of movies (which you have mapped to the shows property of the ApiResult)
You need to change the getMovies function so that the right generic type can be inferred and the json decoder can do the right thing
func getMovies(){
NetworkManger.shared.get(.showList, urlString: "") { [weak self] (result: Result<ApiResult,ErroMessage> ) in
guard let self = self else { return }
switch result{
case .success(let apiResult):
self.movies = apiResult.shows
DispatchQueue.main.async {self.tableView.reloadData()}
case .failure(let error):
print(error.localizedDescription)
}
}
}

How to get data from array using Alamofire to display in CollectionCell?

I am creating an app which requires to pull data from API, the scenario is, I will get the json data below:
[
{
"chargeId": "33fbbbd0-2e33-11e9-a2cb-8a27ecbbcb73",
"chargeDate": "2019-02-12T03:28:44.000",
"vatRate": "NON-VAT",
"taxRate": 0.1,
"policyGroup": "Patient Participation",
"itemDescription": "Ecg at er/icu df",
"scdFlag": 0,
"amounts": null,
"scdDiscounts": 0,
"othDiscounts": 4.54,
"adjustment": 0,
"pfBill": 222.46,
"vatAmount": 0,
"taxableAmount": 11.12,
"merchantDiscount": 0,
"creditedAmount": 211.3,
"chargeAmount": null,
"previousCredits": null
},
{
"chargeId": "5a2cabc1-46c9-11e9-a2cf-863c7cdffd18",
"chargeDate": "2019-03-15T10:24:21.000",
"vatRate": "NON-VAT",
"taxRate": 0.1,
"policyGroup": "Patient Participation",
"itemDescription": "Professional Fees",
"scdFlag": 0,
"amounts": null,
"scdDiscounts": 0,
"othDiscounts": 0,
"adjustment": 0,
"pfBill": 1000,
"vatAmount": 0,
"taxableAmount": 100,
"merchantDiscount": 0,
"creditedAmount": 900,
"chargeAmount": null,
"previousCredits": null
}
]
I did pulled the data successfully by using the Alamofire code below:
typealias getPatientDetailsPerPayoutTaskCompletion = (_ patientDetailsPerPayout: [PatientPayoutDetails]?, _ error: NetworkError?) -> Void
static func getPatientDetailsPerPayout(periodId: Int, doctorNumber: String, parameterName: PatientParameter, hospitalNumber: String, completion: #escaping getPatientDetailsPerPayoutTaskCompletion) {
guard let patientDetailsPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPayoutDetails)?periodId=\(periodId)&doctorNumber=\(doctorNumber)\(parameterName.rawValue)\(hospitalNumber)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(patientDetailsPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
print(patientDetailsPerPayoutURL)
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200:
guard let jsonData = response.data else {
completion(nil, .invalidJSON)
return
}
let decoder = JSONDecoder()
do {
let patientDetailsPayout = try decoder.decode([PatientPayoutDetails].self, from: jsonData)
completion(patientDetailsPayout, nil)
} catch {
completion(nil, .invalidJSON)
}
case 400: completion(nil, .badRequest)
case 404: completion(nil, .noRecordFound)
default:
print("**UNCAPTURED STATUS CODE FROM (getPatientDetailsPayout)\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
The JSON Data will display in a CollectionCell, and user will tapped the cell to view the data under one chargedId but unfortunately, when I tapped the cell the all data are pulled instead of one part of the array only. The code below is what I used to pull just part of the array:
typealias getSelectedPatientItemDetailsTaskCompletion = (_ selectedpatient: PatientPaymentDetails?, _ error: NetworkError?) -> Void
static func getPatientItemDetails(periodId: Int, doctorNumber: String, parameterName: PatientParameter, hospitalNumber: String, chargeId: String, completion: #escaping getSelectedPatientItemDetailsTaskCompletion) {
guard let patientDetailsPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPayoutDetails)?periodId=\(periodId)&doctorNumber=\(doctorNumber)\(parameterName.rawValue)\(hospitalNumber)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(patientDetailsPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
print(patientDetailsPerPayoutURL)
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200:
guard let jsonData = response.data else {
completion(nil, .invalidJSON)
return
}
let decoder = JSONDecoder()
do {
let patientDetailsPayout = try decoder.decode(PatientPaymentDetails.self, from: jsonData)
completion(patientDetailsPayout, nil)
} catch {
completion(nil, .invalidJSON)
}
case 400: completion(nil, .badRequest)
case 404: completion(nil, .noRecordFound)
default:
print("**UNCAPTURED STATUS CODE FROM (getPatientDetailsPayout)\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
}
}
}
Didselect Function to pull data
switch indexPath.section {
case 0:
self.selectedCardIndex = indexPath
let selectedItem = selectedItemDescription.id
getItemDetails(parameter: .searchByChargedId, from: selectedItem)
let cardController = UserCardViewController.init(nibName: "UserCardViewController", bundle: nil)
present(cardController, animated: true, completion: nil)
default: break
}
}
getItemDetails Function
func getItemDetails(parameter: PatientParameter, from: String) {
APIService.PatientList.getPatientItemDetails(periodId: currentRemittance.periodId, doctorNumber: doctorNumber, parameterName: parameter, hospitalNumber: patient.hospitalNumber!, chargeId: from) { (getItem, error) in
guard let pageItemDescription = getItem, error == nil else {
SVProgressHUD.dismiss()
return
}
switch parameter {
case .selectedByChargedID:
if self.patientPayoutDetails == nil {
self.selectedPatientItemDescription = pageItemDescription
}else {
self.patientPayoutDetails.append(contentsOf: pageItemDescription.chargedId)
}
default: break
}
SVProgressHUD.dismiss()
}
}
Hope you can help me, sorry if I included almost all the code but I just want to show you the flow of my codes. Been working on it for almost 1 week. Thank you.
Both of your functions getPatientItemDetails and getPatientDetailsPerPayout are retrieving data from the same URL:
guard let patientDetailsPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPayoutDetails)?periodId=\(periodId)&doctorNumber=\(doctorNumber)\(parameterName.rawValue)\(hospitalNumber)") else {
You probably have different URL for different endpoints; verify the URL for both methods making sure you use the appropriate ones.

error in return response 'cannot call value of non-function type 'NSHTTPURLResponse?' with Swift 3

I read answer from other questions also but not able to solve:
I tried with following way but getting same error at the line of
return response(responseSerializer: responseSerializer,completionHandler: completionHandler)
please help us in how you add #escaping in following method.
public func JSONResponseObject<T: ResponseObjectSerializable>(_ completionHandler: #escaping (DataResponse<T>) -> Void) -> Self {
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else { return .failure(error!) }
let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)
print("result: \(result.value)")
switch result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
if let
response = response,
let responseObject = T(response: response, representation: value as AnyObject)
{
return .success(responseObject)
} else {
let error = Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error: -6006 as! Error)
return .failure(error as! Error)
}
case .failure(let error):
let json = JSON(error)
print("JSON: \(json)")
return .failure(error)
}
}
return response(responseSerializer: responseSerializer,completionHandler: completionHandler)
}
I solved now in following way
I set extension DataRequest { } instead in extension Request { }

Alamofire4 trouble with JSONResponseSerializer & HTTPURLResponse Swift 3.0

Since I updated Alamofire I get the errors: Type Request has no member JSONResponseSerializer and cannot call value of non-function type HTTPURLResponse
I have already switched Response to DataResponse but I still get the error.
Code:
extension Alamofire.Request {
func responseUserEventsArray(_ completionHandler: #escaping (DataResponse<UserEventsWrapper>) -> Void) -> Self {
let responseSerializer = DataResponseSerializer<UserEventsWrapper> { request, response, data, error in
guard error == nil else
{
return .failure(error!)
}
guard let responseData = data else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
}
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .allowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, responseData, error)
switch result {
case .Success(let value):
let json = JSON(value)
let wrapper = UserEventsWrapper()
wrapper.next = json["eventhistory"]["next_page_url"].stringValue
wrapper.previous = json["eventhistory"]["prev_page_url"].stringValue
wrapper.count = json["eventhistory"]["total"].intValue
var allUserEvents:Array = Array<UserEvents>()
print(json)
let results = json["eventhistory"]["data"]
print(results)
for jsonAds in results
{
print(jsonAds.1)
let adsData = UserEvents(json: jsonAds.1, id: Int(jsonAds.0))
allUserEvents.append(adsData)
}
wrapper.usereventsitems = allUserEvents
return .success(wrapper)
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer,completionHandler: completionHandler)
}
}
EDITED
Change
Request.JSONResponseSerializer to DataRequest.jsonResponseSerializer
extension Alamofire.Request to extension Alamofire.DataRequest – Mat0
.success and .failure - FranMowinckel

Alamofire 3->4 trouble with Reponse & ResponseSerializer Swift 3.0

I'm having trouble with the ResponseSerializer I get an unresolved identifier and for Response I get an undeclared type. I've read from alamofire migration doc that Response has been changed to multiple types. So I should change Response->DataReponse but this means I can only pass one argument like:
// What I have
Response(<ListWrapper, NSError>)
// What I should change it to?
DataResponse(<ListWrapper>)
How can I still recieve the Error this way and more importantly how do I migrate the extension to alamofire 4?
My class:
class List{
var idNumber: String?
var title: String?
var posterPath: String?
var release: String?
required init(json: JSON, id: Int?)
{
self.idNumber = json[ListFields.Id.rawValue].stringValue
self.title = json[ListFields.Title.rawValue].stringValue
self.posterPath = json[ListFields.PosterPath.rawValue].stringValue
self.release = json[ListFields.Release.rawValue].stringValue
}
class func setURL_APPEND(_ url: String)
{
URL_APPEND = url
}
// MARK: Endpoints
class func endpointForList() -> String
{
return URL_APPEND
}
fileprivate class func getListAtPath(_ path: String, completionHandler: #escaping (ListWrapper?, NSError?) -> Void) {
Alamofire.request(path)
.responseListArray { response in
if let error = response.result.error
{
completionHandler(nil, error)
return
}
completionHandler(response.result.value, nil)
}
}
class func getList(_ completionHandler: #escaping (ListWrapper?, NSError?) -> Void)
{
getListAtPath(List.endpointForList(), completionHandler: completionHandler)
}
}
// Problem is here:
// for ResponseSerializer I get an unresolved identifier
// and for Response I get an undeclared type
extension Alamofire.Request {
func responseListArray(_ completionHandler: #escaping (Response<ListWrapper, NSError>) -> Void) -> Self {
let responseSerializer = ResponseSerializer<ListWrapper, NSError> { request, response, data, error in
guard error == nil else
{
return .failure(error!)
}
guard let responseData = data else {
let failureReason = "Array could not be serialized because input data was nil."
let error = Alamofire.Error.errorWithCode(.dataSerializationFailed, failureReason: failureReason)
return .failure(error)
}
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .allowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, responseData, error)
switch result {
case .success(let value):
let json = SwiftyJSON3.JSON(value)
let wrapper = ListWrapper()
var allList:Array = Array<List>()
wrapper.totalCount = json["favorite_count"].intValue
// print(json)
let results = json["items"]
// print(results)
for jsonList in results
{
//print(jsonList.1)
let list = List(json: jsonList.1, id: Int(jsonList.0) )
if (list.posterPath == "")
{
continue
}
else
{
//print(movies.posterPath)
allList.append(list)
}
}
wrapper.results = allList
return .success(wrapper)
case .failure(let error):
return .failure(error)
}
}
return response(responseSerializer: responseSerializer,completionHandler: completionHandler)
}
}
Bro try below code see:
func responseListArray(_ completionHandler: #escaping (Response<ListWrapper>) -> Void) -> Self {
let responseSerializer = ResponseSerializer<ListWrapper> { request, response, data, error in
guard error == nil else
{
return .failure(error!)
}
guard let responseData = data else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
}
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .allowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, responseData, error)
switch result {
case .success(let value):
let json = SwiftyJSON3.JSON(value)
let wrapper = ListWrapper()
var allList:Array = Array<List>()
wrapper.totalCount = json["favorite_count"].intValue
// print(json)
let results = json["items"]
// print(results)
for jsonList in results
{
//print(jsonList.1)
let list = List(json: jsonList.1, id: Int(jsonList.0) )
if (list.posterPath == "")
{
continue
}
else
{
//print(movies.posterPath)
allList.append(list)
}
}
wrapper.results = allList
return .success(wrapper)
case .failure(let error):
return .failure(error)
}
}
return response(responseSerializer: responseSerializer,completionHandler: completionHandler)
}

Resources