How to use search with a collection view with different categories?

Trying to make a collection view that has different categories. Each category is populated by a query.

What I am trying to do:

Currently it crashes with this error:

this class is not key value coding-compliant for the key reviewCount.

And if I remove “reviewCount” from the query I get this:

this class is not key value coding-compliant for the key _highlightResult.


Here is my code: (There’s a lot)

MainVC:

  import Foundation
  import UIKit
  import Firebase
  import AlgoliaSearch
  import SwiftyJSON
  import AFNetworking


 class ExploreVC: UIViewController, UICollectionViewDataSource,UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {



var exploreCategories: [exploreCategory]?



@IBOutlet weak var collectionView: UICollectionView!

override func viewDidLoad() {
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.backgroundColor = UIColor.clear
    collectionView.register(categoryCell.self, forCellWithReuseIdentifier: "category")
    exploreCategories = exploreCategory.setCategories()
}


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

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "category", for: indexPath) as! categoryCell
    cell.exploreCategory = exploreCategories?[indexPath.item]
    return cell
}

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

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let size = CGSize(width: view.frame.width, height: 280)
    
    return size
}

}

Category Cell:

import UIKit
import AlgoliaSearch
import SwiftyJSON
 import AFNetworking
 class categoryCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {


var exploreSearch = [exploreBusiness]()
var businessIndex: AlgoliaSearch.Index!
let query = Query()
var searchId = 0
var displayedSearchId = -1
var loadedPage: UInt = 0
var nbPages: UInt = 0

var exploreCategory: exploreCategory? {
    
    didSet {
        
        if let name = exploreCategory?.name {
            
            categoryLabel.text = name.uppercased()
    
            
        }
        
    }
    
}

private let cellId = "exploreCell"

override init(frame: CGRect) {
    super.init(frame: frame)
    setUpViews()
    populateExploreView()
}


required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}



let categoryLabel: UILabel = {

    let black = UIColor(red:0.29, green:0.29, blue:0.29, alpha:1.0)
    let label = UILabel()
    label.font = UIFont(name: "AvenirNext-DemiBold", size: 14)
    label.textColor = black
    label.text = "TRENDING NEAR YOU"
    label.translatesAutoresizingMaskIntoConstraints = false
    label.numberOfLines = 1
    
    return label

}()

let businessCollectionView: UICollectionView = {
    
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    
    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    collectionView.backgroundColor = UIColor.clear
    return collectionView

}()




func setUpViews(){
    
    
    addSubview(businessCollectionView)
    addSubview(categoryLabel)
    
    businessCollectionView.dataSource = self
    businessCollectionView.delegate = self
    businessCollectionView.showsHorizontalScrollIndicator = false
    
    
    businessCollectionView.register(exploreCell.self, forCellWithReuseIdentifier: cellId)
    
    
    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-16-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": categoryLabel]))
    
    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": businessCollectionView]))
    
    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[categoryLabel(24)][v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": businessCollectionView, "categoryLabel": categoryLabel]))
    
}

func populateExploreView() {
    
    print("POPULATING")
    
    let apiClient = Client(appID: "ID", apiKey: "KEY")
    self.businessIndex = apiClient.index(withName: "INDEX")
    query.hitsPerPage = 10
    query.attributesToRetrieve = ["businessType","businessName","image1"]
    
    
    
    query.aroundLatLng = LatLng(lat: userLat ,lng: userLng)
    query.minimumAroundRadius = 24140
    query.query = "Food & Drink"
    let curSearchId = searchId
    
    
    self.businessIndex.search(self.query, completionHandler: {
        (data, error) in
        
        if curSearchId <= self.displayedSearchId || error != nil {
            print("problem #1")
            print(error?.localizedDescription ?? "Error 1")
            return // Newest query already displayed or error
        }
        
        // Decode JSON
        guard let hits = data!["hits"] as? [[String: AnyObject]] else { return }
        guard let nbPages = data!["nbPages"] as? UInt else { return }
        self.nbPages = nbPages
        
        var tmp = [exploreBusiness]()
        for hit in hits {
            tmp.append(exploreBusiness(json: hit))
            let business = exploreBusiness()
            business.setValuesForKeys(hit)
            self.exploreCategory?.businesses?.append(business)
            print(self.exploreCategory?.businesses?.count)
            
            
        }
        
        //Reload view with the new data
        self.exploreSearch = tmp
        print(tmp)
        //self.collectionView.reloadData()
    })
    self.searchId += 1
    
}






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

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! exploreCell
    cell.business = exploreCategory?.businesses?[indexPath.item]
    
    
    return cell
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let size = CGSize(width: 124, height: frame.height - 30)
    
    return size
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsetsMake(0, 16, 0, 16)
}

}

Model:

import Foundation
import  UIKit
import AlgoliaSearch
import SwiftyJSON
import AFNetworking

class exploreCategory: NSObject {

var name: String?
var businesses: [exploreBusiness]?

var exploreSearch = [exploreBusiness]()
var businessIndex: AlgoliaSearch.Index!
let query = Query()
var searchId = 0
var displayedSearchId = -1
var loadedPage: UInt = 0
var nbPages: UInt = 0

static func setCategories() -> [exploreCategory]{
    
    let foodCategory = exploreCategory()
    foodCategory.name = "Food & Drink"
    
    let shopCategory = exploreCategory()
    shopCategory.name = "Shopping Near You"
    
    let cultureCategory = exploreCategory()
    cultureCategory.name = "Cultural Highlights"
    
    let nightlifeCategory = exploreCategory()
    nightlifeCategory.name = "Get Your Night Started"
    
    
    
    return [foodCategory,shopCategory,cultureCategory,nightlifeCategory]
}


   





class exploreBusiness: NSObject {


var name: String?
//var reviewCount: Int?
var imageURL: String?
var businessType: String?

var objectID: String?

//this json dict will grab the dict objects when you search
var json: [String: AnyObject]

init(json: [String: AnyObject]){
    self.json = json
    
    //this is where you will initilaize your properties with the values returned from the dictionary
    
    
    if let businessName = json["businessName"] as? String{
        self.name = businessName
    }
    
    if let reviewCount = json["reviewCount"] as? Int{
        self.reviewCount = reviewCount
    }
    
    if let image = json["image1"] as? String{
        self.imageURL = image
    }
    
    if let type = json["businessType"] as? String{
        self.businessType = type
    }
    
    
    //You might need objectID for something else but your not going to run searches on it so for now. You can always delete it if it's not necessary
    if let objectID = json["objectID"] as? String{
        self.objectID = objectID
    }
}

required convenience override init() {
    self.init(json: [:])
}




}

Hi Ethan,

Thanks for contacting us. This problem seems to be iOS specific and not related to Algolia. The error that you are getting is normally caused by a link from your storyboard to a non existent property. Check here and here for more info.

Also, we have a new iOS swift library that will simplify your implementation of Algolia into your app. You can checkout the announcement here :slight_smile:

Hey guy
Thanks for the response. But “reviewCount” isn’t being used in storyboards. It’s just coded in, so why would it throw that error?
And also:

Unable to find a specification for InstantSearch-iOS (~> 1.0.0-beta4)

But “reviewCount” isn’t being used in storyboards

Okay, can you please check all instances of reviewCount by doing CMD + SHIFT + F and see if you can find some reference in storyboard or somewhere else? It’s hard for me to find out the reason with the code samples that you gave me, since I don’t see something weird in your code. Also, in the error “this class is not key value coding-compliant for the key”, which class are they referring to?

Unable to find a specification for InstantSearch-iOS (~> 1.0.0-beta4)

Can you please copy paste the content of your PodFile here? Also, try this solution and let me know if it works.

They’re referring to this class:

class exploreBusiness: NSObject {


var name: String?
var reviewCount: Int?
var imageURL: String?
var businessType: String?

var objectID: String?


//this json dict will grab the dict objects when you search
var json: [String: AnyObject]

init(json: [String: AnyObject]){
    self.json = json
    
    //this is where you will initilaize your properties with the values returned from the dictionary
    
    
    if let businessName = json["businessName"] as? String{
        self.name = businessName
    }
    
    if let reviewCount = json["reviewCount"] as? Int{
        self.reviewCount = reviewCount
    }
    
    if let image = json["image1"] as? String{
        self.imageURL = image
    }
    
    if let type = json["businessType"] as? String{
        self.businessType = type
    }
    
    
    //You might need objectID for something else but your not going to run searches on it so for now. You can always delete it if it's not necessary
    if let objectID = json["objectID"] as? String{
        self.objectID = objectID
    }
}

required convenience override init() {
    self.init(json: [:])
    }


}

Found a fix to the crash! Removed NSObject from the class. I am using search for another part of my app and saw that the class had no type. However, I am still having trouble getting the cells to populate. If anyone sees something in the code I posted that can be fixed let me know.

I will post an update to this when I get it working

Update:
Got the cells populated. However, they are not being assigned to the right category.

Hey @ethanj.fx, were you able to solve the problem for getting them assigned to the right category? Did you debug the exploreCategories property to see what’s going on?

Yeah, I solved the problem! I will write a tutorial and post it here soon. This will allow people to have home screens like Airbnb which is pretty cool.

3 Likes

@ethanj.fx That’s really awesome of you, thank you for sharing all this knowledge to others :raised_hands:

1 Like

hello @ethanj.fx can you please help me to figure out what the problem was in my code I was also using same categories id to pass and display the results in list page same like you through array and facets in another array and I am using instant search here and here is my question link categories loading issue