Guides

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 Comments

  • Agsela Budi Kartika Sari

    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?

  • tony Macias

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

  • Srithan

    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

  • Srithan Savela

    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.'

  • Visal Sambo

    it gaves me error 26:03

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

  • Shawn Tucker

    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?

  • Daniel Peach

    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?

  • Hunter Drozd

    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!!

  • Greg Lin

    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)
     }

  • Ricardo Brito

    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

  • Ron Borneo

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

  • Muhammad Shahrukh

    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

  • paul baudrier

    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 ^^"

  • ozan

    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 :)))

  • Hind Abdulla

    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.

  • Raja Nukala

    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?

  • Rhidlor

    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!

  • Ahmad Al-Baqawi

    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!

  • Adrian Cervantes

    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?

Leave a Reply

Your email address will not be published. Required fields are marked *