How to Handle an index out of range error crash

Hello:


I am working with a tutorial that will help me in designing my Mac Os app.


I have the following code that is generating and error "index out of ragne" crash: on line 35.

The array is in a .plist file with 5 items in each array [0,1,2,3,4].


Lines 28 - 38 is supposed to do the following:


  1. It removes all items in the pop-up button, getting rid of the Item1 and Item2 entries.
  2. It adds an item for every product, showing its title.
  3. It selects the first product and the first item of the pop-up button. This makes sure that everything is consistent.


Why is it generating an "index out of range" error when lines 31, 32 adds back the items to the array?



import Cocoa

class ViewController: NSViewController {
    private var overviewViewController: OverviewController?
    private var detailViewController: DetailViewController?
   
    private var products = [Product]()
    var selectedProduct: Product?
   
    @IBAction func valueChanged(_ sender: NSPopUpButton) {
       
        detailViewController?.selectedProduct = selectedProduct
        if let bookTitle = sender.selectedItem?.title,
            let index = products.firstIndex(where: {$0.title == bookTitle}) {
            selectedProduct = products[index]
        }
        overviewViewController?.selectedProduct = selectedProduct
    }
   
    @IBOutlet weak var productsButton: NSPopUpButton!
   
    override func viewDidLoad() {
        super.viewDidLoad()
       
        if let filePath = Bundle.main.path(forResource: "Products", ofType: "plist") {
            products = Product.productsList(filePath)
        }
        //1
        productsButton.removeAllItems()
        //2
        for product in products {
            productsButton.addItem(withTitle: product.title)
        }
        //3
        selectedProduct = products [0]//index out of range error
       
        productsButton.selectItem(at: 0)// Do any additional setup after loading the view.
    }
   
    override var representedObject: Any? {
        didSet {
            // Update the view, if already loaded.
        }
    }
   
    override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
       
        guard let tabViewController = segue.destinationController
            as? NSTabViewController else { return }
       
        for controller in tabViewController.children {
           
            if let controller = controller as? OverviewController {
                overviewViewController = controller
                overviewViewController?.selectedProduct = selectedProduct
            }
            else if let controller = controller as? DetailViewController {
                detailViewController = controller
                detailViewController?.selectedProduct = selectedProduct
            }
        }
       
    }
   
}

Accepted Reply

It seems you have a key error:


Line 22 print statement: productList ##START## [["Imagename": iOS10, "Price": 54.99, "Name": iOS by tutorials, "Description": Learn New Ios, "Audience": Intermediate]] productList ##END##


key is Imagename


But line 39 it is ImageName


          init?(dictionary: [String: Any]) {
            guard let title = dictionary["Name"] as? String,
              let audience = dictionary["Audience"] as? String,
              let descriptionText = dictionary["Description"] as? String,
              let price = dictionary["Price"] as? NSNumber,
              let imageName = dictionary["ImageName"] as? String else {
                return nil
            }



Imagename is not ImageName


Hence, guard let fails, hence you return nil.


Fix that (use Imagename in the guard statement) and it will work

Replies

Clearly, products is an empty array.


So, to test in such a case, add some log:


       if let filePath = Bundle.main.path(forResource: "Products", ofType: "plist") {
            products = Product.productsList(filePath)
            print("filePath", filePath)          // You will know if if let succeeds
        }
            print("products.count", products.count)          // You will know if if let succeeds, and how many products


Tell what you get.


I looked back to struct Product,

            if let productList = NSArray(contentsOfFile: fileName) as? [[String: Any]] { 
              for dict in productList { 
                if let product = Product(dictionary: dict) { 
                  products.append(product) 
                } 
              }

Could you show exactly how plist is defined (you can open plist with text edit to see the html and post it here).

Need to check if it is an array of Dict.

You could also add a print here:

            if let productList = NSArray(contentsOfFile: fileName) as? [[String: Any]] {
              print("productList", productList)    
              for dict in productList {
                if let product = Product(dictionary: dict) {
                  products.append(product)
                }
              }


Tell what you get.

Claude 31 has given you good advice. If there is one thing I learned early on it is to do a conditional check (as described by Claude31) every time I access arrays, strings, or any other objects that will throw an out of range error. While we might think we know what and how many is in an object, and most times we do know, there are always exceptions and those exceptions usually show up when we are certain everything is good. If code after the conditional depends on having something when there isn't, then I also put in code to load a default if the conditional fails. Out of range exceptions are nasty when they happen but easy to avoid.

Hello Claude 31:


Here are the outputs: (I put elipsis . . . where there is a lot of text.


Products.plist


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<array>

<dict>

<key>Audience</key>

<string>This book is for intermediate iOS developers who already know the basics of iOS and Swift development but want to learn the new APIs introduced in iOS 10.

. . .

If you are new to iOS, we suggest you read the iOS Apprentice. That will give you a solid foundation of building iOS apps using Swift from the ground up.</string>

<key>Imagename</key>

<string>ios10</string>

<key>Description</key>

<string>Learn the new iOS 10 APIs with Swift 3.



Covers the new features for developers in Xcode 8 and iOS 10, such as Message Apps, SiriKit, and Memory Debugging.</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>iOS 10 by Tutorials</string>

</dict>

<dict>

<key>Audience</key>

<string>This is a book for complete beginners to Apple’s brand new programming language — Swift 3.


. . .


This is a sister book to the iOS Apprentice; the iOS Apprentice focuses on making apps, while Swift Apprentice focuses on the Swift 3 language itself.</string>

<key>Imagename</key>

<string>swift_apprentice</string>

<key>Description</key>

<string>Beginning programming with Swift 3.



This is a book for complete beginners to Apple’s brand new programming language — Swift 3.</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>Swift Apprentice</string>

</dict>

<dict>

<key>Audience</key>

<string>In this book, you’ll create 4 complete games from scratch:



. . .


The games in the book are made with C#. If you have prior programming experience but are new to C#, don’t worry – the book includes an appendix to give you a crash course on C# syntax.</string>

<key>Imagename</key>

<string>unity</string>

<key>Description</key>

<string>Learn how to make games in Unity: a professional game engine used to create games like City Skylines, Hearthstone, the Long Dark, and more.



In this book, you’ll create 4 complete games from scratch.</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>Unity Games by Tutorials</string>

</dict>

<dict>

<key>Audience</key>

<string>The iOS Apprentice is a series of epic-length tutorials for beginners where you’ll learn how to build 4 complete apps from scratch.




You’re not going to create quick example programs that demonstrate how to accomplish a single feature. Instead, you’ll develop complete, fully-formed apps that are good enough to submit to the App Store!</string>

<key>Imagename</key>

<string>ios_apprentice</string>

<key>Description</key>

<string>Learn how to make iPhone and iPad apps from the ground up, with a series of epic-length tutorials for beginners!</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>iOS Apprentice</string>

</dict>

<dict>

<key>Audience</key>

<string>Through a series of mini-games and challenges, you will go from beginner to advanced and learn everything you need to make your own 3D game!



This book is for beginner to advanced iOS developers. Whether you are a complete beginner to making iOS games, or an advanced iOS developer looking to learn about SceneKit, you will learn a lot from this book!

</string>

<key>Imagename</key>

<string>scene_kit</string>

<key>Description</key>

<string>Learn how to make 3D games in Swift, using Apple’s built-in 3D game framework, SceneKit.</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>3D Apple Games by Tutorials</string>

</dict>

<dict>

<key>Audience</key>

<string>This book is for intermediate iOS developers who already know the basics of iOS and Swift 3 development but want to learn how to use Core Data to save data in their apps.



Start with with the basics like setting up your own Core Data Stack all the way to advanced topics like syncing with iCloud, migration, performance, multithreading, and more!</string>

<key>Imagename</key>

<string>core_data</string>

<key>Description</key>

<string>Take control of your data in iOS apps using Core Data, Apple’s powerful object graph and persistence framework.</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>Core Data by Tutorials</string>

</dict>

<dict>

<key>Audience</key>

<string>This book is for iOS developers who already know the basics of iOS and Swift 3, and want to dive deep into animations.



Start with basic view animations and move all the way to layer animations, animating constraints, view controller transitions, and more!</string>

<key>Imagename</key>

<string>animations</string>

<key>Description</key>

<string>Learn how to make iOS Animations in Swift 3 through a series of hands-on tutorials and challenges.</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>iOS Animations by Tutorials</string>

</dict>

<dict>

<key>Audience</key>

<string>In this book, you will make 6 complete and polished mini-games, from an action game to a puzzle game to a classic platformer!



This book is for beginner to advanced iOS developers. Whether you are a complete beginner to making iOS games, or an advanced iOS developer looking to learn about SpriteKit, you will learn a lot from this book!



This book does require some basic knowledge of Swift.</string>

<key>Imagename</key>

<string>2d_games</string>

<key>Description</key>

<string>Learn how to make iOS, tvOS, macOS and watchOS games using Swift 3 and SpriteKit.</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>2D Apple Games by Tutorials</string>

</dict>

<dict>

<key>Audience</key>

<string>This book is for intermediate iOS developers who already know the basics of iOS and Swift development but want to learn how to make Apple Watch apps for watchOS 3.

. . .

If you are new to iOS, we suggest you read the iOS Apprentice. That will give you a solid foundation of building iOS apps using Swift 3 from the ground up.</string>

<key>Imagename</key>

<string>watchos</string>

<key>Description</key>

<string>Learn all about the WatchKit framework, snapshots, notifications, complications, and much, much more!</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>watchOS by Tutorials</string>

</dict>

<dict>

<key>Audience</key>

<string>

. . .


. . .



Updated all chapters to be compatible with the Xcode 8.

</string>

<key>Imagename</key>

<string>tvos</string>

<key>Description</key>

<string>Learn how to make tvOS apps from the ground up, with a series of tutorials for complete beginners!</string>

<key>Price</key>

<real>54.99</real>

<key>Name</key>

<string>tvOS Apprentice</string>

</dict>

</array>

</plist>


FilePath:


filePath /Users/wlionelwilliams/Library/Developer/Xcode/DerivedData/XC5RWStore-aiexogcucdcyahcvmoepmfsyjdsi/Build/Products/Debug/XC5RWStore.app/Contents/Resources/Products.plist



Products.count:


products.count 0


Product List:


productList [["Price": 54.99, "Description": Learn the new iOS 10 APIs with Swift 3.


Covers the new features for developers in Xcode 8 and iOS 10, such as Message Apps, SiriKit, and Memory Debugging., "Audience": This book is for intermediate iOS developers who already know the basics of iOS and Swift development but want to learn the new APIs introduced in iOS 10

.......


(A lot of paragraphs are here


"Name": tvOS Apprentice, "Price": 54.99]]


------------------

filePath /Users/wlionelwilliams/Library/Developer/Xcode/DerivedData/XC5RWStore-aiexogcucdcyahcvmoepmfsyjdsi/Build/Products/Debug/XC5RWStore.app/Contents/Resources/Products.plist


products.count 0


Fatal error: Index out of range

You may also have some issue with NSArray(contentsOfFile: fileName)

which is now marked deprecated.


You could have to use a fileURL


    let fileURL = Bundle.main.url(forResource: "Products", withExtension: "plist")!
    products = Product.productsList(fileURL)

and change the Products func accordingly with

    let productList = try? Array(contentsOf: url)
// continue
catch {
}

So, from the prints, we see there are no product in the products.

But the filePath String is correctly read.


            if let productList = NSArray(contentsOfFile: fileName) as? [[String: Any]] { 
              print("productList", productList)    


seems to give an empty text, but I'm not sure how to read the output.


Could you complement the print like this:

            if let productList = NSArray(contentsOfFile: fileName) as? [[String: Any]] {
              print("productList ##START##", productList, "productList ##END##")   

However there is something unclear.


I read, in the logs

Products.count:


But cannot see

print("Products.count:"

anywhere.


Could you post the code with the prints, to be sure of how to read them ?


As an explanation, I suspect it is what I described in Nov 5, 2019 2:01 AM post, and that path should be replaced by URL.

You are not showing the core part of the issue.


Please show whole definition of your `Product` type.

The method `Product.productsList(_:)` may have some flaw and returning an empty Array, even when your plist contains several elements.

Here are some prints results: BEFORE EDIT from fileURL to filePath


NOTE: I had to comment out // selectedProduct = products [0] to get this to run. [Here is where the app crashes with "index out of range"]



Products.Swift


iflet productList = NSArray(contentsOfFile: filePath) as? [[String: Any]] {
                 print("productList", productList) //Print Here
              for dict in productList {
                if let product = Product(dictionary: dict) {
                  products.append(product)
                }
        }
            print("Products is", products) //Print Here
            return products
          }

RESULT: productList [["Description": Learn New Ios, "Name": iOS by tutorials, "Imagename": iOS10, "Price": 54.99, "Audience": Intermediate]]

Products is []


ViewController: NSViewController --
     if let filePath = Bundle.main.path(forResource: "Products", ofType: "plist") {
          products = Product.productsList(filePath)
             print("filePath", filePath) //Print Here
        }
print("products count", products.count)

filePath: /Users/wlionelwilliams/Library/Developer/Xcode/DerivedData/XC5RWStore-aiexogcucdcyahcvmoepmfsyjdsi/Build/Products/Debug/XC5RWStore.app/Contents/Resources/Products.plist

products count: 0

When I change the filePath to file URL, I get this error in ONE Line:

       if let fileURL = Bundle.main.url(forResource: "Products", withExtension: "plist") {
            products = Product.productsList(fileURL) // Error: Cannot convert value of type 'URL' to expected argument type 'String'
          print("filePath", fileURL)
        }

Could you complement the print like this:


            if let productList = NSArray(contentsOfFile: fileName) as? [[String: Any]] { 
             print("productList ##START##", productList, "productList ##END##")    


Result:

productList ##START## [["Price": 54.99, "Imagename": iOS10, "Audience": Intermediate, "Name": iOS by tutorials, "Description": Learn New Ios]] productList ##END##


Note: I cut the plist down to one record.

Hello OOper:


Here is the Products file:


import Cocoa
 
struct Product {
 
          let title: String
          let audience: String
          let descriptionText: String
          let price: NSNumber
          var image: NSImage? {
            get {
              let i = NSImage(named: imageName)
              return i
            }
          }
 
          fileprivate let imageName: String
 
          static func productsList(_ filePath: String) -> [Product] {
            var products = [Product]()
 
            if let productList = NSArray(contentsOfFile: filePath) as? [[String: Any]] {
                 print("productList ##START##", productList, "productList ##END##")
              for dict in productList {
                if let product = Product(dictionary: dict) {
                  products.append(product)
                }
              }
            }
            print("Products is", products)
            return products
           
          }
 
          init?(dictionary: [String: Any]) {
            guard let title = dictionary["Name"] as? String,
              let audience = dictionary["Audience"] as? String,
              let descriptionText = dictionary["Description"] as? String,
              let price = dictionary["Price"] as? NSNumber,
              let imageName = dictionary["ImageName"] as? String else {
                return nil
            }
 
            self.title = title
            self.audience = audience
            self.descriptionText = descriptionText
            self.price = price
            self.imageName = imageName
          }
}
 
 
class Products: NSViewController {
 
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
     }
    
}


Line 22 print statement: productList ##START## [["Imagename": iOS10, "Price": 54.99, "Name": iOS by tutorials, "Description": Learn New Ios, "Audience": Intermediate]] productList ##END##

Of course, if you call with URL without adapting the func (or creating a new one with the correct argument), it will not work.


However, I tested with a path, and it works (even though the deprecation).


What is the output, on the same reduced list, of

            print("Products is", products)

The result of line 22 shows that you have successfully read the contents of Products.plist into `productList`.


So, there's somethng wrong around here:

              for dict in productList {
                if let product = Product(dictionary: dict) {
                  products.append(product)
                }
              }

You should better check the result of `Product(dictionary: dict)`, as there's no room to make unexpected bahavior.


          init?(dictionary: [String: Any]) {
            guard let title = dictionary["Name"] as? String,
              let audience = dictionary["Audience"] as? String,
              let descriptionText = dictionary["Description"] as? String,
              let price = dictionary["Price"] as? NSNumber,
              let imageName = dictionary["ImageName"] as? String else {
                print("Initialization of Product failed with \(dictionary)") //<-
                return nil
            }
   
            self.title = title
            self.audience = audience
            self.descriptionText = descriptionText
            self.price = price
            self.imageName = imageName
          }

Add the print statement at the failing case of the initializer and you will see where you have a flaw.


By the way, when you want to read such structs from a plist file, Codable would be a good alternative.

struct Product: Codable {
    let price: Double
    let title: String
    let descriptionText: String
    let audience: String
    fileprivate let imageName: String
    var image: NSImage? {
      get {
        let i = NSImage(named: imageName)
        return i
      }
    }
    
    private enum CodingKeys: String, CodingKey {
        case price = "Price"
        case title = "Name"
        case descriptionText = "Description"
        case audience = "Audience"
        case imageName = "Imagename"
    }
}

extension Product {
    static func productsList(_ filepath: String) -> [Product] {
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: filepath))
            let productList = try PropertyListDecoder().decode([Product].self, from: data)
            print(productList)
            return productList
        } catch {
            print(error)
            return []
        }
    }
}

Hello OOper:


Look at the following debug output:

overridefunc viewDidLoad() {
        super.viewDidLoad()
   
       if let filePath = Bundle.main.path(forResource: "Products", ofType: "plist") {
         print("filePath", filePath) //1
         products = Product.productsList(filePath)
             print("products count", products.count) //2
        }
        productsButton.removeAllItems()
       for product in products {
            productsButton.addItem(withTitle: product.title)
            selectedProduct = products [0]
            print(selectedProduct!)
        }
       // selectedProduct = products [0]
        productsButton.selectItem(at: 0)// Do any additional setup after loading the view.
    }

//1

filePath /Users/wlionelwilliams/Library/Developer/Xcode/DerivedData/XC5RWStore-aiexogcucdcyahcvmoepmfsyjdsi/Build/Products/Debug/XC5RWStore.app/Contents/Resources/Products.plist


//2

products count 0


fileprivatelet imageName: String
 
          static func productsList(_ filePath: String) -> [Product] {
            var products = [Product]()
 
            if let productList = NSArray(contentsOfFile: filePath) as? [[String: Any]] {
                 print("productList ##START##", productList, "productList ##END##")//1
              for dict in productList {
                if let product = Product(dictionary: dict) {
                  products.append(product)
                }
              }
            }
            print("Products is", products)//2
            return products
           
          }
1//

productList ##START## [["Imagename": ios10, "Audience": Primary, "Description": Learn the new iOS 10 APIs with Swift 3.


Covers the new features for developers in Xcode 8 and iOS 10, such as Message Apps, SiriKit, and Memory Debugging., "Name": iOS 10 by Tutorials, "Price": 54.99], ["Price": 54.99, "Imagename": swift_apprentice, "Name": Swift Apprentice, "Description": Beginning programming with Swift 3.


This is a book for complete beginners to Apple’s brand new programming language — Swift 3., "Audience": Secondary], ["Imagename": unity, "Description": watchOs, "Name": Unity Games by Tutorials, "Price": 54.99, "Audience": Advanced], ["Imagename": ios_apprentice, "Description": iPhone, "Name": iOS Apprentice, "Audience": College, "Price": 54.99], ["Price": 54.99, "Imagename": scene_kit, "Audience": PostGrad, "Name": 3D Apple Games by Tutorials, "Description": iPhone1], ["Imagename": core_data, "Audience": Advanced, "Description": iPad, "Price": 54.99, "Name": Core Data by Tutorials], ["Imagename": animations, "Name": iOS Animations by Tutorials, "Price": 54.99, "Description": iPod, "Audience": Technical], ["Price": 54.99, "Audience": Scientific, "Description": tvOs, "Imagename": 2d_games, "Name": 2D Apple Games by Tutorials]] productList ##END##

Initialization of Product failed with ["Imagename": ios10, "Audience": Primary, "Description": Learn the new iOS 10 APIs with Swift 3.


Covers the new features for developers in Xcode 8 and iOS 10, such as Message Apps, SiriKit, and Memory Debugging., "Name": iOS 10 by Tutorials, "Price": 54.99]

//2

Products is []


     init?(dictionary: [String: Any]) {
            guard let title = dictionary["Name"] as? String,
              let audience = dictionary["Audience"] as? String,
              let descriptionText = dictionary["Description"] as? String,
              let price = dictionary["Price"] as? NSNumber,
              let imageName = dictionary["ImageName"] as? String else {
                 print("Initialization of Product failed with \(dictionary)")
                return nil
            }

Initialization of Product failed with ["Price": 54.99, "Imagename": swift_apprentice, "Name": Swift Apprentice, "Description": Beginning programming with Swift 3.


This is a book for complete beginners to Apple’s brand new programming language — Swift 3., "Audience": Secondary]

Initialization of Product failed with ["Imagename": unity, "Description": watchOs, "Name": Unity Games by Tutorials, "Price": 54.99, "Audience": Advanced]

Initialization of Product failed with ["Imagename": ios_apprentice, "Description": iPhone, "Name": iOS Apprentice, "Audience": College, "Price": 54.99]

Initialization of Product failed with ["Price": 54.99, "Imagename": scene_kit, "Audience": PostGrad, "Name": 3D Apple Games by Tutorials, "Description": iPhone1]

Initialization of Product failed with ["Imagename": core_data, "Audience": Advanced, "Description": iPad, "Price": 54.99, "Name": Core Data by Tutorials]

Initialization of Product failed with ["Imagename": animations, "Name": iOS Animations by Tutorials, "Price": 54.99, "Description": iPod, "Audience": Technical]

Initialization of Product failed with ["Price": 54.99, "Audience": Scientific, "Description": tvOs, "Imagename": 2d_games, "Name": 2D Apple Games by Tutorials]

-------------------

Use of Codable as in :

struct Product: Codable{// Error: Type 'Product' does not conform to protocol 'Encodable'
                          // Error: Type 'Product' does not conform to protocol 'Decodable'
          let title: String
          let audience: String
          let descriptionText: String
          let price: NSNumber
          var image: NSImage? {
            get {
              let i = NSImage(named: imageName)
              return i
            }
          }

results in error: 1. Type 'Product' does not conform to protocol 'Encodable'

2. Type 'Product' does not conform to protocol 'Decodable'


Currently, I am not sure what all this shows in relation to the original issue: App crash -- Integer out of range.


for product in products {
            productsButton.addItem(withTitle: product.title)
            print(selectedProduct!)
        }
        
            selectedProduct = products [0]// (app crash) integer out of range



FOR CONTEXT, BOTH ViewController: NSView Controller and Products.swift are given above.

It seems you have a key error:


Line 22 print statement: productList ##START## [["Imagename": iOS10, "Price": 54.99, "Name": iOS by tutorials, "Description": Learn New Ios, "Audience": Intermediate]] productList ##END##


key is Imagename


But line 39 it is ImageName


          init?(dictionary: [String: Any]) {
            guard let title = dictionary["Name"] as? String,
              let audience = dictionary["Audience"] as? String,
              let descriptionText = dictionary["Description"] as? String,
              let price = dictionary["Price"] as? NSNumber,
              let imageName = dictionary["ImageName"] as? String else {
                return nil
            }



Imagename is not ImageName


Hence, guard let fails, hence you return nil.


Fix that (use Imagename in the guard statement) and it will work

Hello OOper:

That's exactly the problem. Fixing that resolved all the issues.


Thanks Very Much!!!

Great it is fixed.


You have some general lesson to learn from this.


When your code does not yield the expected result, or crashes, you should:

- find exactly where the error is: here it was the crash point, but that could be a value different from expected

- understand why it crashes :

either by reading carefully the crash log: here it said explicitely that index was out of range

or by putting a print: here it was to print productsList.count to find it was 0

When you print, give it a label so that you can track easily ; I oten build my print loags as

print(#function, "1. productsList count is", productsList.count)

So you get viewDidLoad. 1. productsList count is 0

- from here you need to nail down to find the root cause.

Why is the array empty ?

So look where you built the array by appending

add a print statement after each append

- and finally you end into the init? and see you uit from guard

- you may then add a print to test each clause of the guard, until you pinpoint that

imageName = dictionary["ImageName"] as? String

is nil

- At this point it is obvious to find the root error.


That's a chase, lot of fun in fact !