Sorry, let me be more clear and try to answer all your new questions.
This is a brand new app, that contains six in-app purchases (three consumables, three non-consumables). The first time I submitted the app, I was given the option to select in-app purchases, and selected all six available IAP. I was rejected, and fixed an issue where a poor internet connection could cause an issue with loading IAP.
From that point, the next three times I submitted the app for review, I was not given the option to select IAP to include with submitting the app. I did not check the status of the IAP separately - which is clearly something I should have done. I've submitted dozens of apps over the past 7 years, but this is my first with IAP, so my prior mental workflows took over on this one.
Each of those next three times, my app was rejected because the in-app purchases could not be purchased. The problem area of the code seems to be this line:
iflet product = IAPProducts.store.levelPackProducts.first(where: { $0.productIdentifier == levelPack.productIdentifier ?? "" }),
let regularPrice = product.regularPrice {
...
} else {
selectedLockedPack = levelPack
loadingAlert = displayLoadingIAPAlert()
IAPProducts.store.loadIAP()
}
That levelPackProducts is filled when the following occurs (which should happen both at app startup and when the network state changes, as well as if there is no IAP match:
public func loadIAP() {
isLoadingIAP = true
requestProducts { [weak self] success, products in
guard let self = self else {
return
}
if success, let products = products {
print("finished loading products")
self.iapLoaded = true
self.tipProducts = products.filter({ IAPProducts.tipProductIdentifiers.contains($0.productIdentifier) }).sorted(by: { $0.price.floatValue < $1.price.floatValue })
self.levelPackProducts = products.filter({ IAPProducts.packProductIdentifiers.contains($0.productIdentifier) }).sorted(by: { $0.price.floatValue < $1.price.floatValue })
self.isLoadingIAP = false
NotificationCenter.default.post(name: Notifications.productsLoaded, object: nil)
}
}
}
Note - all of this code works as expected in TestFlight. I can only make an issue happen if I use the Network Link Conditioner and set it to 100% packet loss (even setting it to a really bad connection works). Every rejection has had to do with a matching IAP not being found when a user tries to initiate a purchase (line 1 of the top code sample). Currently the loading IAP alert (line 6 of the top code sample) just stays on the screen spinning, and as far as I can tell it is because the second code sample keeps returning an empty array.
At this point, I am able to "re-submit" the latest binary of the app, App Store connect has no issue with me doing that (I've re-selected that build but have not clicked the "Submit for Review" button). I could also submit a new binary, but it would be the exact same code at this point. I am again not given the option to select IAP to review with my binary when submitting the binary for review - instead, this time, I edited one of the IAP English descriptions, and all of those IAP are marked as "Waiting for Review" - which, as far as I know, this is the first time it as said that.
At this point, should I resubmit (or push a new binary and submit) my app? Or should I be waiting on the IAP to be approved before I submit my app? I really feel based on all my testing, and the incremental changes I have made to my app after each rejection, that the issue is the IAP not being approved before the app is tested - so nothing is being returned when my app is tested. Is that a valid possibility? I'm more than open to needing to look more at my code - but I'm honestly not sure what to even try next, as everything works perfectly in TestFlight (fresh install, several different devices, every time).