8 Replies
      Latest reply on Oct 7, 2016 8:31 AM by balistrerin
      balistrerin Level 1 Level 1 (0 points)

        I am pulling in JSON data from a url and displaying my data in labels in a .xib. The .xib is being called as a NSCollectionViewItem via the identifier "test". Instead of each of my items being displayed it is only showing the last element of the array. Any help would be greatly appreciated.

         

        ViewController.swift

        
        import Cocoa
        class ViewController: NSViewController {
          
            var products: [Product]?
          
            override func viewDidLoad() {
              
                super.viewDidLoad()
                }
            override var representedObject: Any? {
                didSet {
                /
                }
            }
        }
        extension ViewController: NSCollectionViewDataSource {
          
            func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
              
                if let count = products?.count {
                    return count
                }
                return 3
            }
            func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
              
                let item = collectionView.makeItem(withIdentifier: "test", for: indexPath)
              
                return item
            }
        }
        

         

        test.swift

        
        import Cocoa
        class test: NSCollectionViewItem {
            @IBOutlet weak var label: NSTextField!
            @IBOutlet weak var label2: NSTextField!
           
            var names: [String] = []
            var prices: [String] = []
           
           
            override func viewDidLoad() {
                super.viewDidLoad()
                /
               
               
               
               
                let requestURL: NSURL = NSURL(string: "http:/
                let urlRequest: NSMutableURLRequest = NSMutableURLRequest(url: requestURL as URL)
                let session = URLSession.shared
                let task = session.dataTask(with: urlRequest as URLRequest) {
                    (data, response, error) -> Void in
                   
                    let httpResponse = response as! HTTPURLResponse
                    let statusCode = httpResponse.statusCode
                   
                    if (statusCode == 200) {
                       
                        do{
                           
                            let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments)
                           
                            if let products = json as? [[String: Any]] {
                               
                                for product in products {
                                   
                                   
                                    if let name = product["product_name"] as? String {
                                       
                                       
                                        if let price = product["product_price"] as? String {
           
                                           
                                            self.names.append(name as String)
                                            self.prices.append(price as String)
                                           
                                           
                                                DispatchQueue.main.async() {
                                                   
                                                    for (index, _) in self.names.enumerated() {
                                                        self.label.stringValue = self.names[index]
                                                    }
                                                    for (index, _) in self.prices.enumerated() {
                                                        self.label2.stringValue = self.prices[index]
                                                    }
                                                   
                                                }
                                           
                                           
                                            print(name,price)
                                           
                                           
                                        }
                                    }
                                }
                               
                            }
                           
                        }catch {
                           
                            print("Error with Json: \(error)")
                        }
                       
                    }
                }
               
                task.resume()
               
               
               
            }
           
        }
        class Product: NSObject {
           
            var id: NSNumber?
            var product_name: String?
            var product_price: String?
           
        }
        
        • Re: How to loop through array and display in NSCollectionViewItem
          QuinceyMorris Level 8 Level 8 (6,050 points)

          Well, it's doing what you're telling it to do.

           

          First of all it is incorrect (apparently, based on the code you've shown) to retrieve the JSON data from an instance of your NSCollectionViewItem subclass. There is going to be one instance per item. You are retrieving the JSON data over and over again in each item, each time throwing away the previous data.

           

          Second, instead of putting the decoded JSON data in your view controller's "products" array, you're saving the individual values (name, price) inside arrays in the NSCollectionViewItem instances. Thus, your "products" array is never going to have the correct count, nor the correct product details (unless something happens in code you don't show us).

           

          Third, by iterating "for product in products" per item, you set the value of the item view labels at each iteration, which of course leaves only the last set of label values to be displayed.

           

          You should start familiarizing yourself with the MVC (model-view-controller) design pattern, and that should help you start to structure your code properly:

           

               developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html

            • Re: How to loop through array and display in NSCollectionViewItem
              balistrerin Level 1 Level 1 (0 points)

              Thank you very much for your response, this was very helpful.  I see how my JSON is being retrieved once per item.  I guess I still need to learn to proper way to access my JSON data.  My inital thoughts of putting my JSON data into the NSCollectionViewItem subclass was the fact that my labels representing my product_name and product_price can only be linked via an outlet in my test.swift file.  I am not sure how to access it from another class. 

               

              The second issue i had a feeling I wasnt doing the product count correctly. 

               

              The third issue I can see how I am leaving the last set of values everytime.  My issue is that I am developing this app on MAC OS X not iOS and the syntax for mobile vs desktop is drastically different and I know that the same basic idea and MVC model should aply but I am having a hard time finding some docuemntation or tutorials showing how to parse JSON currectly in a NSCollectionView for desktop applications.  If you have any other documentation or help on this subject that would be greatly appreciated. 

              • Re: How to loop through array and display in NSCollectionViewItem
                balistrerin Level 1 Level 1 (0 points)

                I worked on some of my code to what I think is the correct way to do it but now I have no errors, no warnings, and a completely blank window when I run my project.  Neither of my lables show up and it is completely blank.  my test.xib file has two labels that are not being populated.  My arrays are still being printed out in the console correctly.  I noticed that in iOS they use item.label.stringValue = self.names[indexPath.row].  In this case "row"  was not a member to use.  I have a feeling something is wrong with my code using .item and casting it as a String.  Any help of suggestions would be greatly appreciated.  Here is my updated code:

                 

                test.swift

                
                import Cocoa
                class test: NSCollectionViewItem {
                    @IBOutlet weak var label: NSTextField!
                    @IBOutlet weak var label2: NSTextField!
                
                    override func viewDidLoad() {
                        super.viewDidLoad()
                    
                    }
                }
                
                

                 

                ProductModel.swift

                
                import Cocoa
                class ProductModel: NSObject {
                
                        var id: NSNumber?
                        var product_name: String?
                        var product_price: String?
                     
                }
                
                

                 

                ViewController.swift

                
                import Cocoa
                class ViewController: NSViewController, NSCollectionViewDelegate {
                  
                    var products: [ProductModel]?
                  
                    var names: [String] = []
                    var prices: [String] = []
                  
                    override func viewDidLoad() {
                      
                        super.viewDidLoad()
                      
                        getJSON()
                      
                    }
                  
                    func getJSON() {
                      
                        let requestURL: NSURL = NSURL(string: "http:/
                        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(url: requestURL as URL)
                        let session = URLSession.shared
                        let task = session.dataTask(with: urlRequest as URLRequest) {
                            (data, response, error) -> Void in
                          
                            let httpResponse = response as! HTTPURLResponse
                            let statusCode = httpResponse.statusCode
                          
                            if (statusCode == 200) {
                              
                                do{
                                  
                                    let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments)
                                  
                                    if let products = json as? [[String: Any]] {
                                      
                                        for product in products {
                                          
                                          
                                            if let name = product["product_name"] as? String {
                                              
                                              
                                                if let price = product["product_price"] as? String {
                                                  
                                                  
                                                    self.names.append(name as String)
                                                  
                                                    self.prices.append(price as String)
                                                  
                                                }
                                            }
                                        }
                                      
                                    }
                                  
                                    print("Name Array: \(self.names)", "Price Array: \(self.prices)")
                                  
                                  
                                  
                                }catch {
                                  
                                    print("Error with Json: \(error)")
                                }
                              
                            }
                        }
                      
                        task.resume()
                    }
                    override var representedObject: Any? {
                        didSet {
                        /
                        }
                    }
                }
                extension ViewController: NSCollectionViewDataSource {
                  
                    func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
                      
                        return names.count
                    }
                    func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
                      
                        let item: test = collectionView.makeItem(withIdentifier: "test", for: indexPath) as! test
                            item.label.stringValue = self.names[indexPath.item] as String
                            item.label2.stringValue = self.prices[indexPath.item] as String
                        return item
                    }
                }
                
                
                  • Re: How to loop through array and display in NSCollectionViewItem
                    QuinceyMorris Level 8 Level 8 (6,050 points)

                    The likely cause of the empty collection display is that you've done nothing to inform the collection view, when the JSON retrieval completes, that the number of items has changed. As far as the collection view is concerned, the number of items is still the same as when it first asked via collectionView(_:numberOfItemsInSection:), that is, zero. Because loading the JSON data is asynchronous, the actual number of items isn't know until much later.

                     

                    After you've loaded the data you should inform the collection view by calling reloadData().

                      • Re: How to loop through array and display in NSCollectionViewItem
                        balistrerin Level 1 Level 1 (0 points)

                        Are you saying I need to add some sort of completion handler to my json code?  And then afterwards reload()?

                          • Re: How to loop through array and display in NSCollectionViewItem
                            QuinceyMorris Level 8 Level 8 (6,050 points)

                            No, I'm saying that immediately after the "for product in products" loop you've finished updating the arrays. At that point you can call reloadData().

                             

                            Note, however, that it may be necessary to call reloadData() from the main thread, if it causes a UI update directly. (The documentation isn't specific on this point.) If that's so, and if your URLSession delegate queue is something other than the main queue, you will need to dispatch the reloadData() call back to the main queue.

                              • Re: How to loop through array and display in NSCollectionViewItem
                                balistrerin Level 1 Level 1 (0 points)

                                Thank you so much for the help.  I really appreciate you taking the time to explain the process to me.  I have defintiely learned a lot! 

                                  • Re: How to loop through array and display in NSCollectionViewItem
                                    balistrerin Level 1 Level 1 (0 points)

                                    In case this helps anyone I am ging to post my code again with the correct MVC format.  This code is successfully displaying in a collectionView using a .xib file as my NSCollectionViewItem.  I hope this helps someone who needs any help with displaying JSON data in a NSCollectionView for MAC Desktop Applications.  Thank you to QuinceyMorris for your guidance.

                                     

                                    ProductModel.swift

                                    
                                    import Cocoa
                                    class ProductCategory: NSObject{
                                    
                                        var name: String?
                                    
                                        var product: [ProductModel]?
                                    }
                                    class ProductModel: NSObject {
                                    
                                            var id: NSNumber?
                                            var product_name: String?
                                            var product_price: String?
                                            var product_description: String?
                                            var product_image: String?
                                            var product_video: String?
                                            var product_download: String?
                                    }
                                    

                                     

                                    test1.swift

                                    
                                    import Cocoa
                                    class test1: NSCollectionViewItem {
                                        @IBOutlet weak var label: NSTextField!
                                        @IBOutlet weak var label2: NSTextField!
                                        @IBOutlet weak var label3: NSTextField!
                                      
                                        var productItem: ProductModel?
                                      
                                      
                                        var buildProduct: ProductModel? {
                                          
                                            didSet{
                                                label.stringValue = (buildProduct?.product_name)!
                                                label2.stringValue = (buildProduct?.product_price)!
                                                label3.stringValue = (buildProduct?.product_image)!
                                              
                                              
                                            }
                                        }
                                      
                                        override func viewDidLoad() {
                                            super.viewDidLoad()
                                          
                                        }
                                    }
                                    

                                     

                                    ViewController.swift

                                    
                                    import Cocoa
                                    class ViewController: NSViewController, NSCollectionViewDelegate {
                                       
                                        @IBOutlet weak var colView: NSCollectionView!
                                       
                                        var productCategories: [ProductCategory]?
                                       
                                        var productsArray: [ProductModel]?
                                       
                                        var names: [String] = []
                                        var prices: [String] = []
                                        var images: [String] = []
                                       
                                        override func viewDidLoad() {
                                           
                                            super.viewDidLoad()
                                           
                                       
                                            getJSON()
                                           
                                            colView.dataSource = self
                                            colView.delegate = self
                                           
                                            let nib = NSNib(nibNamed: "test1", bundle: nil)
                                            colView.register(nib, forItemWithIdentifier: "test1")
                                           
                                           
                                        }
                                       
                                        func getJSON() {
                                           
                                            let requestURL: NSURL = NSURL(string: "http:/
                                            let urlRequest: NSMutableURLRequest = NSMutableURLRequest(url: requestURL as URL)
                                            let session = URLSession.shared
                                            let task = session.dataTask(with: urlRequest as URLRequest) {
                                                (data, response, error) -> Void in
                                               
                                                let httpResponse = response as! HTTPURLResponse
                                                let statusCode = httpResponse.statusCode
                                               
                                               
                                               
                                                if (statusCode == 200) {
                                                   
                                                    do{
                                                       
                                                        let json = try JSONSerialization.jsonObject(with: data!, options:.mutableContainers)
                                                       
                                                        self.productsArray = [ProductModel]()
                                                       
                                                        if let products = json as? [[String: Any]] {
                                                           
                                                            for product in products {
                                                               
                                                                let testproduct = ProductModel()
                                                                testproduct.product_name = product["product_name"] as? String
                                                                testproduct.product_price = product["product_price"] as? String
                                                                testproduct.product_image = product["product_image"] as? String
                                                                self.productsArray?.append(testproduct)
                                                               
                                                            }
                                                           
                                                            DispatchQueue.main.async(){
                                                            self.colView.reloadData()
                                                            }
                                                        }
                                                       
                                                    }catch {
                                                       
                                                        print("Error with Json: \(error)")
                                                    }
                                                   
                                                }
                                            }
                                           
                                            task.resume()
                                        }
                                        override var representedObject: Any? {
                                            didSet {
                                            /
                                            }
                                        }
                                    }
                                    extension ViewController: NSCollectionViewDataSource {
                                       
                                        func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
                                           
                                            return productsArray?.count ?? 0
                                        }
                                        func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
                                           
                                            let item = collectionView.makeItem(withIdentifier: "test1", for: indexPath) as! test1
                                           
                                                item.buildProduct  = productsArray?[indexPath.item]
                                           
                                            return item
                                        }
                                    }