Decodable Podcasts and URLEncoding Requests
Podcasts
With the data coming back to us from the iTunes API formatted in JSON, we'll have to transform it into Podcast model objects in our program. Luckily for us, this process has been streamlined with the introduction of the Decodable protocol, making is super easy to parse JSON objects into our own. In addition, we'll look into a bug where search for anything with a space results in a bad request. To solve this issue, we'll need to use a more powerful Alamofire request that allows us to perform some URLEncoding on our url. Enjoy.

Comments (11)
Tokyojogo
5 years ago
Hi Brian, great lesson. I got some questions. 1. I was taught before that when using decodable, the drawback is that we need to get everything from the JSON exactly. So putting it inside [results] solves this issue? 2. how come the searchResults struct is not in its own data model file? 3. How do we handle no results? Thank you! Loving this course...
Brian Voong
5 years ago
Tokyojogo
5 years ago
Thanks Brian!
이동건
5 years ago
Hello Brian I could search your podcast by "Brian voong" but i couldn't search by "brian voong" is this correct? will you gonna fix this error later videos?
이동건
5 years ago
sorry it works :)
syedfa
5 years ago
Brian, thanks so much for putting together such amazing courses. There are many subtle concepts that I learn from each episode, it's amazing. I do have a question for you: How do I search for a particular podcast episode? You point out how to search for a particular podcast, but how about implementing a similar search for a particular episode? What search parameter/key could I submit to allow me to filter results for episodes for a particular podcast? Just wondering. Thanks so much once again for your contributions. Please keep up the good work!
Kenny Ho
5 years ago
Brian's tutorials are quality content!
Benedito Mauro
5 years ago
Model: struct StreamCell: Decodable { var idStream: Int? var streamTitle: String? var numOfPlays: Int64? var streamArtwork: String? var streamLocation: String? var streamArtist: Int? private enum CodingKeys: String, CodingKey { case idStream = "id_stream" case streamTitle = "stream_title" case numOfPlays = "num_of_plays" case streamArtwork = "stream_artwork" case streamLocation = "stream_location" case streamArtist = "stream_artist" } init(from decoder: Decoder) throws { (without this I get an error } Decodable: let streams = try JSONDecoder().decode([StreamCell].self, from: data) print("Titulo: ", streams) let str = String(data: data, encoding: .utf8) print(str) Output: Retrived Info: [music_app.StreamCell(idStream: nil, streamTitle: nil, numOfPlays: nil, streamArtwork: nil, streamLocation: nil, streamArtist: nil), music_app.StreamCell(idStream: nil, streamTitle: nil, numOfPlays: nil, streamArtwork: nil, streamLocation: nil, streamArtist: nil)] Json: Optional("[{\"id_stream\":1,\"stream_title\":\"Padecer\",\"num_of_plays\":455,\"artist_artwork\":\"/Users/maurobenedito/Desktop/music_images/artist_picture/artist_picture\",\"stream_artist\":\"1\",\"stream_location\":\"dcfghvbjknlm,\",\"stream_artwork\":\"/Users/maurobenedito/Desktop/music_images/artwork/img_music1\"},{\"id_stream\":2,\"stream_title\":\"Fogo Fogo Incendio\",\"num_of_plays\":45,\"artist_artwork\":\"/Users/maurobenedito/Desktop/music_images/artist_picture/artist_picture\",\"stream_artist\":\"1\",\"stream_location\":\"dcfghvbjknlm,\",\"stream_artwork\":\"/Users/maurobenedito/Desktop/music_images/artwork/img_music1\"}]") Why am I getting everything nill? No exception error was thrown therefore JSonDecoder worked but my array comes with nothing. Any help please
Benedito Mauro
5 years ago
Server response: [ { "id_stream":1, "stream_title":"Padecer", "num_of_plays":455, "artist_artwork":"/Users/maurobenedito/Desktop/music_images/artist_picture/artist_picture", "stream_artist":"1", "stream_location":"dcfghvbjknlm,", "stream_artwork":"/Users/maurobenedito/Desktop/music_images/artwork/img_music1" }, { "id_stream":2, "stream_title":"Fogo Fogo Incendio", "num_of_plays":45, "artist_artwork":"/Users/maurobenedito/Desktop/music_images/artist_picture/artist_picture", "stream_artist":"1", "stream_location":"dcfghvbjknlm,", "stream_artwork":"/Users/maurobenedito/Desktop/music_images/artwork/img_music1" } ]
Brian Voong
5 years ago
Brian Voong
5 years ago
Benedito Mauro
5 years ago
Thank you Mr Brian
Aigars Sukurs
5 years ago
Hi! I'm trying to follow along using a different data source but stuck on this JSON key "2003-01-01". How to get rid of this error: "Expected to decode Dictionary<String, Any> but found an array instead." struct Show: Decodable { id: Int } struct Results: Decodable { let error: Bool let data: [String: [Show]] } { "error": false, "data": { "2003-01-01": [{ "id": "1105240", "starts": "2003-01-01 06:00:00", "ends": "2003-01-01 06:15:00", "channel": "1", "catid": "227" },{ "id": "1105241", "starts": "2003-01-01 06:15:00", "ends": "2003-01-01 06:30:00", "channel": "1", "catid": "227" }], "2003-01-02": [{ "id": "1105310", "starts": "2003-01-02 06:00:00", "ends": "2003-01-02 06:15:00", "channel": "1", "catid": "227" },{ "id": "1105311", "starts": "2003-01-02 06:15:00", "ends": "2003-01-02 06:30:00", "channel": "1", "catid": "227" }] } }
Brian Voong
5 years ago
Muhammad Junaid
5 years ago
hey Brian I got this error even after watching your videos several time and compare my code with yours code . It works without space but after adding space in searchBar it gives following error : dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.})))
petar7
5 years ago
Hi Brian after finishing this course I will try to make music player downloader , could we use Apple Music API to download music and play it offline , do I need prepaid account for this and if it is even possible because I founded many songs in Apple Music that I could not find in other API offers.
Brian Voong
5 years ago
JamesDrummond
4 years ago
Hi Brian, following along with this course again with Xcode 10 and iOS 12.1 beta, but I don't know if something has changed within swift, however JSONDecoder is just spitting out 'Failed to decode: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No value."' seemingly at random whenever it feels like it. Sometimes you can type really quick and it does it, sometimes you can type one character, sometimes you can just hit the backspace. Can't for the life of me work it out as it shouldn't have changed all too much.
halloffame
4 years ago
I am getting the same error, but it seems to actually be an issue with the iTunes search API. For certain terms I get back a 403 response code from the API with an empty body, which is where the error is coming from I believe. I wonder if maybe the API is rate limited.
Kritbovorn Taweeyossak
4 years ago
Update for AF func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { let url = "https://itunes.apple.com/search" let parameters = ["term": searchText] AF.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseData { (responseData) in if let err = responseData.error { print("Failed to connect yahoo", err) return } guard let data = responseData.data else { return } do { let searchResult = try JSONDecoder().decode(SearchResults.self, from: data) self.podcasts = searchResult.results self.tableView.reloadData() }catch let decodeErr { print("Failed to decode: ", decodeErr) } } }
Christopher J. Roura
3 years ago
Hi Brian, For the search throttling can we use the new notification center styled approach. I am referring to the method in which you showed search throttling in the maps text field. I am not sure if that would apply here but was curious to know. Thanks as always for the great tutorials. This is the code I mean: NotificationCenter.default .publisher(for: UITextField.textDidChangeNotification, object: searchTextField) .debounce(for: .milliseconds(500), scheduler: RunLoop.main) .sink { (_) in self.performLocalSearch() }
Brian Voong
3 years ago
Christopher J. Roura
3 years ago
Thank you!
HELP & SUPPORT