Search code examples
iosswiftxcodecrash-reportsstorekit

Understanding iOS stack trace in crash logs


I have a strange situation where my app loads its in-app store view fine in testing including via testflight, but when downloaded from the appstore it crashes. I've found the relevant crash log below but I'm not sure exactly which parts relate to my code vs things happening under the hood.

This is the stack trace:

Thread 4 Crashed:
0   My app          0x00000001001b7908 Swift runtime failure: Index out of range + 0 (<compiler-generated>:0)
1   My app          0x00000001001b7908 subscript.get + 20 (<compiler-generated>:0)
2   My app          0x00000001001b7908 specialized Store.productsRequest(_:didReceive:) + 1376
3   My app          0x00000001001b75e8 list.get + 28 (Store.swift:0)
4   My app          0x00000001001b75e8 specialized Store.productsRequest(_:didReceive:) + 576
5   My app          0x00000001001b5d8c productsRequest + 12 (<compiler-generated>:0)
6   My app          0x00000001001b5d8c @objc Store.productsRequest(_:didReceive:) + 76
7   StoreKit                        0x00000001ab0f8070 __27-[SKProductsRequest _start]_block_invoke_2 + 136 (SKProductsRequest.m:104)
8   libdispatch.dylib               0x000000018f5404b4 _dispatch_call_block_and_release + 32 (init.c:1518)
9   libdispatch.dylib               0x000000018f541fdc _dispatch_client_callout + 20 (object.m:560)
10  libdispatch.dylib               0x000000018f5450c8 _dispatch_queue_override_invoke + 788 (inline_internal.h:2632)
11  libdispatch.dylib               0x000000018f553a6c _dispatch_root_queue_drain + 396 (inline_internal.h:0)
12  libdispatch.dylib               0x000000018f554284 _dispatch_worker_thread2 + 164 (queue.c:7052)
13  libsystem_pthread.dylib         0x00000001d49e4dbc _pthread_wqthread + 228 (pthread.c:2631)
14  libsystem_pthread.dylib         0x00000001d49e4b98 start_wqthread + 8

From this I'm reading that there's something going wrong in the call to productsRequest so here is that function:

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        let myProduct = response.products
        for product in myProduct {
            list.append(product)
        }
        // Update labels
        localTitle = list[0].localizedTitle
        localDescription = list[0].localizedDescription
        
        // Format the price and display
        let formatter = NumberFormatter()
        formatter.locale = Locale.current
        formatter.numberStyle = .currency
        if let formattedPrice = formatter.string(from: list[0].price){
            localPrice = ("Upgrade: \(formattedPrice)")
            delegate?.storeUpdateReceived(store: self)
        }
    }

Here's what I'm not sure about though. Can we identify where within that function the issue is? Or is the issue getting triggered before we actually step into lines within the function? I can't repro the crash outside of the live environment so I don't think I can add breakpoints in a live installed app.

I see the index out of range error, but is that likely caused by where I'm referencing the first element in my lists (where maybe it's empty)? Or could that index be something else?


Solution

  • From the available information, we can assume it is crashing on the line where you are referring the first element of list. It will surely crash if the list is empty, you can write it like this:

     func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
            let myProduct = response.products
            for product in myProduct {
                list.append(product)
            }
            // Update labels
            localTitle = list.first?.localizedTitle ?? ""
            localDescription = list.first?.localizedDescription ?? ""
            
            // Format the price and display
            let formatter = NumberFormatter()
            formatter.locale = Locale.current
            formatter.numberStyle = .currency
            if let price = list.first?.price, let formattedPrice = formatter.string(from: price){
                localPrice = ("Upgrade: \(formattedPrice)")
                delegate?.storeUpdateReceived(store: self)
            }
        }