Swift: AppStore – Integrating REST API, JSON, and Model Objects (Ep 2)



For the API calls, please use these two links:
https://api.letsbuildthatapp.com/appstore/featured
https://api.letsbuildthatapp.com/appstore/appdetail

In this video, we go over an example of how a real world application integrates with a REST api to retrieve data. By the end of this tutorial, you’ll be able to build an application that is very similar to what the Apple App Store looks like.

JSON API URL:
https://api.letsbuildthatapp.com/appstore/featured

Recreating App Store Part 1:

Twitter Slide Out Menu Course
https://www.letsbuildthatapp.com/course/Twitter-Slide-Out-Menu

Podcasts Course
https://www.letsbuildthatapp.com/course/Podcasts

Intermediate Training Core Data
https://www.letsbuildthatapp.com/course/intermediate-training-core-data

Instagram Firebase Course
https://www.letsbuildthatapp.com/course/instagram-firebase

Facebook Group
https://www.facebook.com/groups/1240636442694543/

Source Code:
https://www.letsbuildthatapp.com/course/AppStore

Completed source code can be found here:
https://www.letsbuildthatapp.com/course/appstore

34 thoughts on “Swift: AppStore – Integrating REST API, JSON, and Model Objects (Ep 2)”

  1. can u help me? I'm using swift 5 and It's appear "Value of type 'Any' has no subscript" in for dict json["categories'] how can I fix that?

    Reply
  2. i get this error when trying to setValuesforkeyswithDictionary." this class is not key value coding-compliant for the key type"

    Reply
  3. Please help

    Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppStore.AppCategory 0x600000295360> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key type.'

    I think I got his after 25:00

    Reply
  4. I'm getting this error. Please help

    Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppStore.AppCategory 0x600000295360> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key type.'

    Reply
  5. it gaves me error 26:03

    setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key type.

    Reply
  6. Could not cast value of type '__NSCFConstantString' (0x1b5b6a068) to 'NSArray' (0x1b5b6a950).
    2018-02-19 09:57:58.052147-0500 play3[1721:466939] Could not cast value of type '__NSCFConstantString' (0x1b5b6a068) to 'NSArray' (0x1b5b6a950).
    (lldb)
    AnyOne know how to get pass this error?

    Reply
  7. When we put a collectionView in a collectionViewCell, does that cell become the sub-collectionView’s view controller? It seems all the controller stuff we did for category cell in ViewController, we are doing for AppCell in CategoryCell. is there a way to avoid this? As in point the AppCell to a separate AppCell View controller, and do the controller stuff there instead?

    Reply
  8. Brian, why is the JSON database structured like:
    – categories: {
    – {
    name: "Best New Apps"
    – apps: {….}
    }

    instead of:
    – categories: {
    – Best New Apps: {
    – Telepaint: { …. }
    }

    I try recreating your structuring using Firebase code and its basically impossible (or so it seems) if I want to add a new app to a category using Firebase code I try:
    let ref = Database.database().child("categories").child(???????????) there is no child. I find it impossible to reference one of the categories because it is a blank un-named group node.

    Say I want to add a new app to one of the categories using my data structure:
    let ref = Database.database().child("categories").child("Best New Apps")
    //updateChildValues by adding a new app

    Thanks for the help in advance!!

    Reply
  9. To fix JSON parsing in Swift 4 create a new struct as follow:

    struct FeaturedApps: Decodable {
        let bannerCategory: AppCategory
        let categories: [AppCategory]
    }

    Then change AppCategory and App from class to struct and confirm "Decodable" Protocol.

    Add a CodingKeys enum to App struct as following:

    struct App: Decodable {
        let id: Int?
        let name: String?
        let category: String?
        let imageName: String?
        let price: Double?
        
        enum CodingKeys: String, CodingKey {
            case id = "Id"
            case name = "Name"
            case category = "Category"
            case imageName = "ImageName"
            case price = "Price"
        }
    }

    Lastly, fetch data from server by doing:

    let featuredApps = try JSONDecoder().decode(FeaturedApps.self, from: data)
    let appCategories = featuredApps.categories
    DispatchQueue.main.async {
                        completionHandler(appCategories)
     }

    Reply
  10. Great tutorial… 2 episodes in and I learnt a lot… I was able to get my code working. The problem with swift is that it is changing fast…
    in your version if I use setValue(value, forKey: key), I will get an exception and the app will stop… but if I use super.setValue(value, forKey: key) it works fine. Is there any reason for that? Thanks again for such a great tutorial

    Reply
  11. Fixing the text box height seemed a little tedious for a common issue. Is there now an easier way since Swift 4 came out?

    Reply
  12. Hello Brian, your videos have been a great help. One question, what api service are you using to create the json URL, and what is the best free way for us to make our own? Thanks a lot

    Reply
  13. Thanks buddy, i just have one question : What if i dont have any JSON, but i want many sample cells in my CollectionView ? I have to deliver my project in 2 days sooooooo ^^"

    Reply
  14. For crushing issues 25:12, I am updating the Solution.

    Use swift4 methods instead deprecated methods, here is what I have done in model.swift and main swift files.

    Remove all lines in Models.swift file and replace them with those lines:

    * Models.swift
    import UIKit

    // swift4 ile daha rahat bir model kullanımı mevcut olduğu için Decodable protocol'ünü kullanacağız
    struct allCategories: Decodable {
    let bannerCategory: lineOfApp?
    let categories: [lineOfApp]?
    }

    struct lineOfApp: Decodable {
    let name: String?
    let apps: [inLineOfApp]?
    let type: String?

    // dinamik olarak aldığımız veriler
    static func fetchFeaturedApps(customCompletionHandler: @escaping (allCategories) -> Void) {
    // url stringimizi URL objesine çeviriyoruz
    let jsonUrl = "https://api.letsbuildthatapp.com/appstore/featured"
    guard let url = URL(string: jsonUrl) else { fatalError("error") }

    // url verilerini almaya başlıyoruz.
    URLSession.shared.dataTask(with: url) { (data, response, error) in
    if error != nil {print(error!);return}
    do {
    // bu kısımda, urlsession ile aldığmız data yı JSONDecoder ile pars edeceğiz
    let decodedApps = try JSONDecoder().decode(allCategories.self, from: data!)
    DispatchQueue.main.async {
    customCompletionHandler(decodedApps)
    }
    }catch let err {
    print("Error serializing json: (err)")
    }
    }.resume()
    }

    }

    struct inLineOfApp: Decodable {
    let id: Double?
    let Name: String?
    let Category: String?
    let ImageName: String?
    let Price: Double?
    }

    and this is the Main View Controller, Replace it with that one:

    import UIKit

    class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

    private let cellId = "cellId"

    var categories: allCategories?
    var lineApp: [lineOfApp]?

    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    lineOfApp.fetchFeaturedApps { (data) in
    self.categories = data
    self.lineApp = data.categories
    self.collectionView?.reloadData()
    }

    collectionView?.backgroundColor = UIColor.white
    collectionView?.alwaysBounceVertical = true
    collectionView?.register(CategoryCell.self, forCellWithReuseIdentifier: cellId)
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as? CategoryCell else {
    fatalError("unresolved cell ID")
    }

    cell.appCategory = lineApp?[indexPath.item]

    return cell

    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if let count = lineApp?.count { return count }
    return 0
    }

    // CategoryCell ile yarattığımız kutuların default 50×50 boyutlarını costumize edeceğiz.
    // burası ana satırlarımız, film rulosu veya flow diyorum buralara
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: collectionView.frame.width, height: 230)
    }

    }

    cheers :)))

    Reply
  15. Hi Brian, first thanks for the great videos, I have learned a lot from you. I have one question, why didn't you use multiple sections. 1 for each category in this example? I am confused as to what situation where its best to use multiple sections.

    Reply
  16. I am on Swift 4 (XCode 9) and 25:12, line #34, the XCode is showing the error, Type 'Any' has no subscript members. can someone help how to fix that?

    Reply
  17. I'm really looking forward to programming using Swift, I'm sure your videos will become a large asset for me in the future; thanks!

    Reply
  18. amazing workout and such a hard challenge. go it to work! you definitely need SwiftyJSON coco pod to make this as easier task.

    thank you Bain!

    Reply
  19. Error on Swift 3.1 -> Argument labels '(_:, _:)' do not match any available overloads

    this one is in the final part of the video, min 36:20

    let rect = NSString(string: name).boundingRect(with: CGSize(frame.width, 1000), options:NSStringDrawingOptions.usesFontLeading.union(NSStringDrawingOptions.usesLineFragmentOrigin), attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 12)], context: nil)

    CGSize(frame.width, 1000). <- Argument labels '(_:, _:)' do not match any available overloads

    How can I fix this??? -> Xcode put automatically -> dictionaryRepresentation as an argument
    like this..

    CGSize(dictionaryRepresentation: frame.width, 1000) <- but the same..,

    someone can help?

    Reply

Leave a Comment