Alamofire.request().responseObject doesn't return response - swift

I use AlamofireObjectMapper extension. I want to get response, but it's failed. My tableLeague object is always nil because code with response closure doesn't call ({ (response: DataResponse) in
if response.result.isSuccess {
tableLeague = response.result.value
} doesn't call). I use next methods:
let header = ["X-Auth-Token":"1231231"]
static let sharedInstance = ServerAPI()
private func getRequest(uri: String) -> DataRequest {
return Alamofire.request(uri, method: .get, headers: header)
}
public func getTableLeague() -> TableLeague {
var tableLeague: TableLeague?
getRequest(uri: URL).responseObject { (response: DataResponse<TableLeague>) in
if response.result.isSuccess {
tableLeague = response.result.value
}
}
return tableLeague!
}
And use in business class:
public func readTableLeague() -> TableLeague {
let tableLeague = ServerAPI.sharedInstance.getTableLeague()
return tableLeague
}
I think it can be because response haven't yet but i try to set object that i haven't yet
Whats a problem? Completion handlers i need to use else?

Please try to map by following structure
class Response: Mappable {
var success: Bool?
var data: [Data]?
required init?(_ map: Map) {
mapping(map)
}
func mapping(map: Map) {
success <- map["success"]
data <- map["data"]
}
}
class Data: Mappable {
var uid: Int?
var name: String?
// add other field which you want to map
required init?(_ map: Map) {
mapping(map)
}
func mapping(map: Map) {
uid <- map["uid"]
name <- map["name"]
}
}
When you get JSON response simple map by this syntax
let response = Mapper<Response>().map(responseObject)
if let id = response?.data?[0].uid {
println(id)
}

Related

Convert object mapper model class to json string in swift

class LeadsEntity: NSObject, Mappable {
var name: String?
var mobile: String?
var address: String?
var coords: String?
var stime: String?
override init() {}
required init?(map: Map) {}
func mapping(map: Map) {
name <- map["name"]
mobile <- map["mobile"]
address <- map["address"]
coords <- map["coords"]
stime <- map["stime"]
}
}
I am using this method to convert model to json string
func json(from object: Any) -> String? {
let JSONString = (object as! [LeadsEntity]).toJSONString(prettyPrint: true)?.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
return JSONString
}
but it converts like this
I am not able to remove \ and I also tried to remove by .replacingOccurrences(of: "\", with: "", options: NSString.CompareOptions.literal, range:nil) but no success. Thanks in advance.
Give him the model here (type: T.self) and put the Data here (from: data)
do {
let decoded = self.decodeJSON(type: T.self, from: data)
} catch {}
func decodeJSON<T: Decodable>(type: T.Type, from: Data?) -> T? {
let decoder = JSONDecoder()
guard let data = from else { return nil }
do {
let objects = try decoder.decode(type.self, from: data)
return objects
} catch let jsonError {
print("Failed to decode response JSON", jsonError)
return nil
}
}

Generic delegate response handlers

I've got a class currently something like this
class Client {
var responseOneDelegate: ResponseOneDelegate?
var responseTwoDelegate: ResponseTwoDelegate?
...
func onData(forMessageType messageType: MessageType, data: Data) {
switch messageType {
case .responseOne:
let response = JSONDecoder().decode(Response<ResultForResponseOne>.self, from: data)
responseOneDelegate?.received(response: response)
case .responseTwo:
let response = JSONDecoder().decode(Response<ResultForResponseTwo>.self, from: data)
responseTwoDelegate?.received(response: response)
}
}
}
protocol ResponseOneDelegate {
func received(response: Response<ResultForResponseOne>)
}
protocol ResponseTwoDelegate {
func received(response: Response<ResultForResponseTwo>)
}
With the idea that a class can be one or multiple delegates
class Handler: ResponseOneDelegate, ResponseTwoDelegate {
func received(response: Response<ResultForResponseOne>) { }
func received(response: Response<ResultForResponseTwo>) { }
}
This seems to be screaming out to be generalised as there will be quite a lot of responses in this format, but I can't quite figure out how to do it
I've tried using a generic type to make just a single delegate
protocol ResponseDelegate: AnyObject {
associatedtype T
func received(response: Response<T>)
}
It doesn't seem possible to store the delegates in Client in [MessageType: ResponseDelegate] so with the idea of the generic delegate I'm not sure how I'd store the references of the delegates? Maybe I'd just have to cast them before calling?
How would you generalise this?
Functions may be helpful here, in cases where you have a protocol with just a single function in it, and few types that implement the protocol.
Here's an example of the idea:
class Client {
var handle: ((Data) -> Bool)
init(handle: #escaping ((Data) -> Bool)) {
self.handle = handle
}
func received(data: Data) {
handle(data)
}
}
let ints = { (data: Data) -> Bool in
guard let i = try? JSONDecoder().decode(Int.self, from: data) else {
return false
}
print("handle \(i)")
return true // if handled
}
let strings = { (data: Data) -> Bool in
guard let str = try? JSONDecoder().decode(String.self, from: data) else {
return false
}
// handle Strings
print("handle \(str)")
return true
}
let intOrString = { (data: Data) -> Bool in
ints(data) ||
strings(data)
}
func handleMany(handlers: ((Data) -> Bool)...) -> (Data) -> Bool {
return { data in
for handle in handlers {
if handle(data) {
return true
}
}
return false
}
}
let intsOrStrings = handleMany(handlers: ints, strings)
let aOrBClient = Client(handle: intsOrStrings)
let aClient = Client(handle: ints)

Passing server response to ViewModel MVVM

I have been trying to pass the response of my AF.request statement to my viewModel but am unable to understand still? I need to pass my response to my ViewModel and then use it to display in the tableView.
This is my Service Class:
Service
class Service {
fileprivate var baseUrl = ""
//https://api.themoviedb.org/3/tv/76479?api_key=3d0cda4466f269e793e9283f6ce0b75e&language=en-US
init(baseUrl: String) {
self.baseUrl = baseUrl
}
var tvShowDetails = TVShowModel()
func getTVShowDeet(completionHandler: #escaping ()-> TVShowModel){
let request = AF.request(self.baseUrl)
.validate()
.responseDecodable(of: TVShowModel.self) { (response) in
guard let tvShow = response.value else {return}
return tvShow
print("printing response", tvShow)
}
}
}
ViewModel
func getTVShowDetails(){
service.getTVShowDeet{
print(self.response)
self.delegate?.reloadTable()
self.headerDelegate?.configureHeader()
print("prinitn respinse in VM", self.response)
}
}
Model
struct TVShowModel : Decodable {
let id : Int?
let original_name : String?
let overview : String?
enum CodingKeys: String, CodingKey {
case id = "id"
case original_name = "original_name"
case overview = "overview"
}
init(){
id = nil
original_name = nil
overview = nil
}
}
Networking requests are asynchronous meaning we don't know when they'll complete so we need to use completion handlers instead of returning from the function (unless you use Async/Await). Something along the lines of this should work:
Service
func getTVShowDeet(completionHandler: #escaping (TVShowModel) -> Void) {
let request = AF.request(self.baseUrl)
.validate()
.responseDecodable(of: TVShowModel.self) { (response) in
guard let tvShow = response.value else { return }
completionHandler(tvShow)
}
}
ViewModel
func getTVShowDetails() {
service.getTVShowDeet { [weak self] tvShow in
// Here you may need to store the tvShow object somewhere to use in your tableView datasource.
self?.delegate?.reloadTable()
self?.headerDelegate?.configureHeader()
}
}

App Crashing with error: generic parameter 'T' could not be inferred

I'm trying to get custom object which is hashable from UserDefault.
My custom model is defined below:
class WorkerProfileResponse: Mappable, Hashable{
static func == (lhs: WorkerProfileResponse, rhs: WorkerProfileResponse) -> Bool {
return lhs.id == rhs.id
}
var hashValue: Int{
return self.id!
}
var id, loginStatus, lastLogin, lastActive: Int?
var username, email, mobileNumber: String?
var userCategories: [String]?
var userSubCategories: [String]?
var biometricToken: String?
var accessToken: AccessToken?
var userStatus: UserStatus?
var userProfile: UserProfile?
required init(map: Map) {
}
func mapping(map: Map) {
id <- map["id"]
loginStatus <- map["is_logged_in"]
lastLogin <- map["last_login"]
lastActive <- map["last_active"]
biometricToken <- map["biometricToken"]
username <- map["username"]
email <- map["email"]
mobileNumber <- map["mobile_number"]
accessToken <- map["accessToken"]
userStatus <- map["userStatus"]
userCategories <- map["userCategories"]
userSubCategories <- map["userSubCategories"]
userProfile <- map["userProfile"]
}
}
My userdefault method is:
class func getModel<T: Hashable>(key: String) -> T {
let decoded = UserDefaults.standard.data(forKey: key)
let decodedModel = NSKeyedUnarchiver.unarchiveObject(with: decoded!) as! T
return decodedModel
}
And I'm calling it like this:
UserDefault.getModel(key: "workerProfile")
App is crashing when I'm calling this method I don't understand the reason, error is:
error: generic parameter 'T' could not be inferred
I'm answering my own question, if it helps anyone in the future.
It was crashing while decoding because there was no value present in userdefault.
This line had the issue because of force casting:
let decodedModel = NSKeyedUnarchiver.unarchiveObject(with: decoded!) as! T
I've changes this method:
class func getModel<T: Hashable>(key: String) -> T {
let decoded = UserDefaults.standard.data(forKey: key)
let decodedModel = NSKeyedUnarchiver.unarchiveObject(with: decoded!) as! T
return decodedModel
}
To this:
class func getModel<T: Hashable>(key: String) -> T? {
let decoded = UserDefaults.standard.data(forKey: key)
if decoded != nil{
let decodedModel = NSKeyedUnarchiver.unarchiveObject(with: decoded!) as! T
return decodedModel
}
else
{
return nil
}
}

Cast object to generic

I am creating a function to be used in different occasions. But for this, I need to Cast the return of a function to the Object that I pass as generic in this main function.
func makeRequestToApi<T>(object: T, url: String) {
Alamofire.request(.GET, url).responseJSON { request in
if let json = request.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
let data = JSON(json)
let object: [T] = self.createProductObject(data) as Any as! [T]
dispatch_async(dispatch_get_main_queue()) {
self.delegate?.networkingDidUpdate(object)
}
}
}
}
}
I thought that I only need to call this way:
networkingController.makeRequestToApi(Product, url: Urls.menu)
This function will return an array of products self.createProductObject(data) -> [Product]
But Xcode make me add .self to the first parameter in makeRequestToApi
networkingController.makeRequestToApi(Product.self, url: Urls.menu)
This way, as I see, Swift will not convert the return of my class to Product as I need it.
Anyone knows what I need to do?
Thank you.
You probably want something like this:
func makeRequestToApi<T>(create: JSON -> [T], url: String) {
Alamofire.request(.GET, url).responseJSON { request in
if let json = request.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
let data = JSON(json)
let object = create(data)
dispatch_async(dispatch_get_main_queue()) {
self.delegate?.networkingDidUpdate(object)
}
}
}
}
}
makeRequestToApi(createProductObject, url: Urls.menu)
EDIT: This compiles for me (you probably have to adjust your delegate method):
import Foundation
struct Product {}
protocol Delegate : class {
func networkingDidUpdate<T>(obj: [T])
}
class Test {
weak var delegate : Delegate?
func makeRequestToApi<T>(create: JSON -> [T], url: String) {
Alamofire.request(.GET, url).responseJSON { request in
guard let json = request.result.value else { return }
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
let object = create(JSON(json))
dispatch_async(dispatch_get_main_queue()) {
self.delegate?.networkingDidUpdate(object)
}
}
}
}
func createProductObject(json: JSON) -> [Product] {
return [Product()]
}
}
let test = Test()
test.makeRequestToApi(test.createProductObject, url: "")