Skip to content

Commit

Permalink
[Tabs] Make -itemAtIndexPath: safe (#7555)
Browse files Browse the repository at this point in the history
This PR makes the -itemAtIndexPath: method on MDCItemBar safe against out of bounds exceptions.

Related to #6275.
  • Loading branch information
andrewoverton committed Jun 13, 2019
1 parent c8d9ab9 commit cb9ec4f
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 5 deletions.
27 changes: 22 additions & 5 deletions components/Tabs/src/private/MDCItemBar.m
Expand Up @@ -357,7 +357,9 @@ - (BOOL)collectionView:(UICollectionView *)collectionView
id<MDCItemBarDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(itemBar:shouldSelectItem:)]) {
UITabBarItem *item = [self itemAtIndexPath:indexPath];
return [delegate itemBar:self shouldSelectItem:item];
if (item) {
return [delegate itemBar:self shouldSelectItem:item];
}
}
}
return YES;
Expand All @@ -368,6 +370,9 @@ - (void)collectionView:(UICollectionView *)collectionView
if (_collectionView == collectionView) {
// Update selected item.
UITabBarItem *item = [self itemAtIndexPath:indexPath];
if (!item) {
return;
}
_selectedItem = item;

// Notify delegate of new selection.
Expand Down Expand Up @@ -400,10 +405,12 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView

MDCItemBarCell *itemCell = [collectionView dequeueReusableCellWithReuseIdentifier:kItemReuseID
forIndexPath:indexPath];
UITabBarItem *item = [self itemAtIndexPath:indexPath];

[self configureCell:itemCell];
[itemCell updateWithItem:item atIndex:indexPath.item count:_items.count];

UITabBarItem *item = [self itemAtIndexPath:indexPath];
if (item) {
[itemCell updateWithItem:item atIndex:indexPath.item count:_items.count];
}

return itemCell;
}
Expand All @@ -416,6 +423,9 @@ - (CGSize)collectionView:(UICollectionView *)collectionView
NSParameterAssert(_collectionView == collectionView);

UITabBarItem *item = [self itemAtIndexPath:indexPath];
if (!item) {
return CGSizeZero;
}

const CGFloat itemHeight = CGRectGetHeight(self.bounds);
CGSize size = CGSizeMake(CGFLOAT_MAX, itemHeight);
Expand Down Expand Up @@ -513,7 +523,11 @@ - (void)selectItemAtIndex:(NSUInteger)index animated:(BOOL)animated {
}

- (UITabBarItem *)itemAtIndexPath:(NSIndexPath *)indexPath {
return _items[indexPath.item];
if (indexPath && indexPath.section == 0 && indexPath.item >= 0 &&
(NSInteger)_items.count > indexPath.item) {
return _items[indexPath.item];
}
return nil;
}

- (NSInteger)indexForItem:(nullable UITabBarItem *)item {
Expand Down Expand Up @@ -628,6 +642,9 @@ - (void)updateSelectionIndicatorToIndex:(NSInteger)index {

// Construct a context object describing the selected tab.
UITabBarItem *item = [self itemAtIndexPath:indexPath];
if (!item) {
return;
}
MDCTabBarPrivateIndicatorContext *context =
[[MDCTabBarPrivateIndicatorContext alloc] initWithItem:item
bounds:selectionIndicatorBounds
Expand Down
51 changes: 51 additions & 0 deletions components/Tabs/tests/unit/MDCItemBarTests.m
@@ -0,0 +1,51 @@
// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>

#import "MDCItemBar.h"

@interface MDCItemBar (Testing)
- (UITabBarItem *)itemAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface MDCItemBarTests : XCTestCase
@end

@implementation MDCItemBarTests

- (void)testItemAtIndexPath {
// Given
MDCItemBar *itemBar = [[MDCItemBar alloc] init];
UITabBarItem *item1 = [[UITabBarItem alloc] initWithTitle:@"first tab" image:nil tag:0];
UITabBarItem *item2 = [[UITabBarItem alloc] initWithTitle:@"second tab" image:nil tag:0];

// When
itemBar.items = @[ item1, item2 ];

// Then
NSIndexPath *indexPathForFirstItem = [NSIndexPath indexPathForItem:0 inSection:0];
XCTAssertNotNil([itemBar itemAtIndexPath:indexPathForFirstItem]);
NSIndexPath *indexPathForSecondItem = [NSIndexPath indexPathForItem:1 inSection:0];
XCTAssertNotNil([itemBar itemAtIndexPath:indexPathForSecondItem]);
NSIndexPath *indexPathForThirdItem = [NSIndexPath indexPathForItem:2 inSection:0];
XCTAssertNil([itemBar itemAtIndexPath:indexPathForThirdItem]);
NSIndexPath *indexPathWithNonZeroSection = [NSIndexPath indexPathForItem:0 inSection:1];
XCTAssertNil([itemBar itemAtIndexPath:indexPathWithNonZeroSection]);
NSIndexPath *indexPathWithItemEqualToNegativeOne = [NSIndexPath indexPathForItem:-1 inSection:0];
XCTAssertNil([itemBar itemAtIndexPath:indexPathWithItemEqualToNegativeOne]);
}

@end

0 comments on commit cb9ec4f

Please sign in to comment.