-1

When trying to remove an unknown type from an NSMutableArray, I'm unsure of how to assign an item to a variable to be removed. I'm able to drill down to the string property of that type but unsure of how to remove the entire object.

Right now the error I'm getting is:

Use of undeclared identifier 'item'

NSMutableArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
    NSLog(@"Found product: %@ %@ %0.2f",
          skProduct.productIdentifier,
          skProduct.localizedTitle,
          skProduct.price.floatValue);


    if ( [skProduct.productIdentifier isEqualToString:@"com.eboticon.Eboticon.baepack1"] ) {
        // do found
        [skProducts removeObject: item];
    } else {
        // do not found
    }
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
Laurence Wingo
  • 3,912
  • 7
  • 33
  • 61
  • 1
    did you copy & paste from different sources? – vikingosegundo Dec 20 '16 at 03:31
  • @vikingosegundo I did from stackoverflow and I can't an understandable resource on here for assigning if part of the statement to a variable so that it can be removed. – Laurence Wingo Dec 20 '16 at 03:35
  • 1
    What is `item` in your posted code? – rmaddy Dec 20 '16 at 03:35
  • 1
    You can't enumerate and modify an array at same time. The easy way is enumerate on a copy. – Bryan Chen Dec 20 '16 at 03:36
  • 1
    After you fix the problem with "item", your code will crash with a new error because you are attempting to modify `skProducts` while you iterate through it. You can't do that. – rmaddy Dec 20 '16 at 03:37
  • @rmaddy I overlooked that I could use 'skProduct' in place of item and I wasn't aware of the side effects of enumerating in objective-c so you all truly helped in making better decisions. – Laurence Wingo Dec 20 '16 at 03:51

2 Answers2

3

Your current issue is, that you never defined item.

You are (fast) enumerating with for (SKProduct * skProduct in skProducts) {, so probably you mean skProduct instead of item.

Once you fixed that you will get a new error: you are not allowed to alter an array while you enumerating over it. see Best way to remove from NSMutableArray while iterating? for solutions for that.

One way: reversed block-based enumeration.

[skProducts enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SKProduct * skProduct, NSUInteger idx, BOOL *stop) {
    if ([skProduct.productIdentifier isEqualToString:@"com.eboticon.Eboticon.baepack1"] ) {
        // do found
        [skProducts removeObject: skProduct];
    } else {
        // do not found
    }
}]; 

Another way: filter all products that do not have the unwanted product identifier.

[skProducts filterUsingPredicate:[NSPredicate predicateWithFormat:@"productIdentifier != %@", @"com.eboticon.Eboticon.baepack1"]];

Another note:

I assume, that response is of class SKProductsResponse. It's products property is defined as @property(nonatomic, readonly) NSArray<SKProduct *> *products;

NSMutableArray * skProducts = response.products;

So skProducts does point to a NSArray, not NSMutableArray, as you are just typing the variable, this does not transform the object the variable is pointing to.

You want something like

NSMutableArray *skProducts = [response.products mutableCopy];
Community
  • 1
  • 1
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
1

In Objective-C you can't mutate an array that you are enumerating (and for...in syntax is enumerating). You'll crash.

You either need to loop through the objects by index backwards, and remove the objects that don't belong, or use the NSArray function filterUsingPredicate. filterUsingPredicate is probably the better way to go, but I don't use NSPredicate often enough to be able to give you code for it off the top of my head.

The for loop version might look like this:

if (skProducts.count == 0)
   return;
for (NSInteger index = skProducts.count - 1; index >= 0; index--) {
  product = skProducts[index];
  if ( [skProduct.productIdentifier isEqualToString:@"com.eboticon.Eboticon.baepack1"] ) {
    //Do whatever you need to do with the object before removing it
    [skProducts removeObjectAtIndex: index];
  }
}
Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • 1
    A few things. 1) Don't use `int`. Use `NSInteger` for the loop. 2) It's dangerous to use `skProducts.count - 1` since `count` is unsigned. If the array is empty, your initial index becomes a really huge number and the app crashes when trying to access the object at that index. – rmaddy Dec 20 '16 at 03:45
  • Good points. Sloppy of me not to use NSInteger, and warn about range checking. – Duncan C Dec 20 '16 at 03:51
  • 1
    Instead of the extra `if` check, what I do (when doing reverse loops like this) is to use `for (NSInteger index = whatever.count; index > 0; index--)` and then all index references become `someArray[index - 1]`. But the `if` check might be safer because my approach requires me to remember the `- 1` for all uses of `index` inside the loop. – rmaddy Dec 20 '16 at 03:54
  • 1
    @rmaddy, I modified my answer to include an initial range check. How would you calculate the index of the last object without count-1 in Objective-C? – Duncan C Dec 20 '16 at 03:55
  • My previous comment explains that. – rmaddy Dec 20 '16 at 03:56
  • 1
    Note the reverse indexing is not the best way to go. Better to allocate a new array and add all items that should not be removed to the new arrray. The benchmarks from other posters report that that is much faster than any approach that removes items from an existing array. Plus my reverse approach does not use fast enumeration, so it will be slower still. – Duncan C Dec 20 '16 at 03:57
  • 1
    This *screams* for the Swift filtered() higher level function, but the OP asked about Objective-C, so never mind. – Duncan C Dec 20 '16 at 03:59
  • 1
    @DuncanC, if speed would make a real difference, the problem would not be the way of iterating, but the sheer number of products. – vikingosegundo Dec 20 '16 at 04:00