I'm new with this mapper thing and too confused. I have an API request, given a Title, the API return this:
{
Response = True;
Search = (
{
Poster = "https://images-na.ssl-images-amazon.com/images/M/MV5BMjAxODQ2MzkyMV5BMl5BanBnXkFtZTgwNjU3MTE5OTE@._V1_SX300.jpg";
Title = ARQ;
Type = movie;
Year = 2016;
imdbID = tt5640450;
},
{
Poster = "N/A";
Title = Arq;
Type = movie;
Year = 2011;
imdbID = tt2141601;
},
{
Poster = "N/A";
Title = "A.R.Q.";
Type = movie;
Year = 2015;
imdbID = tt3829612;
}
);
totalResults = 3;
}
So I've created a mappable class for this result:
class SearchResponse: Mappable {
var isSuccess : String?
var searchArray: [Movie]?
var searchCount: String?
required init?(map: Map) {
}
func mapping(map: Map) {
isSuccess <- map["Response"]
searchArray <- map["Search"]
searchCount <- map["totalResults"]
}
}
class Movie: Mappable {
var posterURL : String?
var title : String?
var runtime : String?
var director : String?
var actors : String?
var genre : String?
var plot : String?
var production : String?
var year : String?
var imdbID : String?
var imdbRating : String?
required init?(map: Map) {
}
func mapping(map: Map) {
posterURL <- map["Poster"]
title <- map["Title"]
runtime <- map["Runtime"]
director <- map["Director"]
actors <- map["Actors"]
genre <- map["Genre"]
plot <- map["Plot"]
production <- map["Production"]
year <- map["Year"]
imdbID <- map["imdbID"]
imdbRating <- map["imdbRating"]
}
}
Question: I mapped this movie class like this, but for the search by title I'll only have 4 of this attributes. But for the next search I'll need all of them. Is that right? Or should I create two separate classes to deal with each kind of response?
Ok! I'm showing the result of this search on my SearchTableViewController. Now I want to show more details of this movie (any movie of the "Search" array on this previous response). To do that, the API offers another type of search, that is search by imdbID. So I've created a segue on my SearchTableViewController to get this ID and pass to my MovieViewController (the view that will show these details):
let searchSegue = "segueFromSearch"
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let searchIndex = tableView.indexPathForSelectedRow?.row
let movie = movies?[searchIndex!]
let selectedImdbID = movie?.imdbID
print("|Table View Controler| Segue. IMDB_ID: \(String(describing: selectedImdbID))")
if segue.identifier == searchSegue {
if let destination = segue.destination as? MovieViewController {
destination.imdbID = selectedImdbID!
print("|Table View Controler| Inside of if let. Debug print: I get til here. imdbID = \(selectedImdbID!)")
}
}
}
My function for this API request is:
//The movieSearched variable is the text typed on my searchBar
let URL = "https://www.omdbapi.com/?s=\(movieSearched)&type=movie"
Alamofire.request(URL).responseObject{ (response: DataResponse<SearchResponse>) in
print("response is: \(response)")
switch response.result {
case .success(let value):
let searchResponse = value
self.movies = (searchResponse.searchArray)
self.searchTableView.reloadData()
case .failure(let error):
let alert = UIAlertController(title: "Error", message: "Error 4xx / 5xx: \(error)", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
OK, given this overview of what I have, let's talk about my problem...
When I search by ID, the Json response is that:
{
Actors = "Robbie Amell, Rachael Taylor, Shaun Benson, Gray Powell";
Awards = "N/A";
BoxOffice = "N/A";
Country = "USA, Canada";
DVD = "16 Sep 2016";
Director = "Tony Elliott";
Genre = "Sci-Fi, Thriller";
Language = English;
Metascore = "N/A";
Plot = "Trapped in a lab and stuck in a time loop, a disoriented couple fends off masked raiders while harboring a new energy source that could save humanity.";
Poster = "https://images-na.ssl-images-amazon.com/images/M/MV5BMjAxODQ2MzkyMV5BMl5BanBnXkFtZTgwNjU3MTE5OTE@._V1_SX300.jpg";
Production = Netflix;
Rated = "N/A";
Ratings = (
{
Source = "Internet Movie Database";
Value = "6.4/10";
},
{
Source = "Rotten Tomatoes";
Value = "60%";
}
);
Released = "16 Sep 2016";
Response = True;
Runtime = "88 min";
Title = ARQ;
Type = movie;
Website = "N/A";
Writer = "Tony Elliott";
Year = 2016;
imdbID = tt5640450;
imdbRating = "6.4";
imdbVotes = "17,481";
}
I did this alamofire request for the search by ID:
func getMovieById() {
let URL = "https://www.omdbapi.com/?i=\(String(describing: imdbID!)))"
Alamofire.request(URL).responseObject{ (response: DataResponse<SearchResponse>) in
print("|MovieController| Response is: \(response)")
let Result = response.result.value
print("Result for search by id: \(String(describing: Result!.searchArray))")
// Have to get the values here, right?
}
}
Obviously, I'm not getting the data that I want it here. So...
Questions:
I'm too confused by so many layers. Also, I'm beginner with swift and I'm using this ObjectMapper for the first time. Sorry for so much code here, but I think I had to explain my scenario.
You have to map each property with correct data type of that property. One of the objects in your response contains a Boolean value ( For ex. "Response"), but you are declaring it as a String. We have to match the data type of the property exactly, else the object will be nil and will not be mapped.
Also search by id response does not match your mapper class.
let Result = response.result.value
is wrong. response.result.value
would yield you SearchResponse
object.
Bottom line
You have to get the mapping part right first. Any mismatching types will be not be mapped. Using response object will give you the whole object with all the mappings instead of the JSON obviously. So it should be: let movie = response.result.value
. Then you access movie's properties like for ex. movie.actors