├── .gitignore ├── .slather.yml ├── .travis.yml ├── GroupedArraySample ├── GroupedArraySample-Mac │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── main.m ├── GroupedArraySample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── GroupedArraySample-Mac.xcscheme │ │ └── GroupedArraySample-iOS.xcscheme ├── GroupedArraySample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── INTUFruit.h │ ├── INTUFruit.m │ ├── INTUFruitCategory.h │ ├── INTUFruitCategory.m │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── GroupedArraySampleTests │ └── Info.plist └── GroupedArrayTests-Mac │ └── Info.plist ├── INTUGroupedArray.podspec ├── Images ├── INTUGroupedArray-Illustration.png └── INTUGroupedArray.png ├── LICENSE ├── README.md ├── Source ├── INTUGroupedArray │ ├── INTUGroupedArray.h │ ├── INTUGroupedArray.m │ ├── INTUGroupedArrayImports.h │ ├── INTUMutableGroupedArray.h │ ├── INTUMutableGroupedArray.m │ └── Internal │ │ ├── INTUGroupedArrayDefines.h │ │ ├── INTUGroupedArrayInternal.h │ │ ├── INTUGroupedArraySectionContainer.h │ │ ├── INTUGroupedArraySectionContainer.m │ │ ├── INTUIndexPair.h │ │ └── INTUMutableGroupedArrayInternal.h └── Swift │ └── GroupedArray.swift ├── SwiftGroupedArray ├── SwiftGroupedArray-Bridging-Header.h ├── SwiftGroupedArray.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── SwiftGroupedArray │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── SwiftGroupedArrayTests-Bridging-Header.h └── SwiftGroupedArrayTests │ ├── Info.plist │ └── SwiftGroupedArrayTests.swift └── Tests ├── INTUGroupedArrayTests.m └── INTUMutableGroupedArrayTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /.slather.yml: -------------------------------------------------------------------------------- 1 | ci_service: travis_ci 2 | coverage_service: coveralls 3 | xcodeproj: GroupedArraySample/GroupedArraySample.xcodeproj 4 | source_directory: Source 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: objective-c 3 | os: osx 4 | xcode_project: GroupedArraySample/GroupedArraySample.xcodeproj 5 | xcode_sdk: iphonesimulator11.0 6 | osx_image: xcode9 7 | 8 | before_install: 9 | - gem install slather --no-ri --no-rdoc 10 | 11 | 12 | script: set -o pipefail && xcodebuild test -project GroupedArraySample/GroupedArraySample.xcodeproj -scheme GroupedArraySample-iOS -destination 'platform=iOS Simulator,name=iPhone 8 Plus,OS=11.0' 13 | 14 | after_success: slather 15 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample-Mac/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | @interface AppDelegate : NSObject 31 | 32 | 33 | @end 34 | 35 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample-Mac/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import "AppDelegate.h" 29 | 30 | @interface AppDelegate () 31 | 32 | @property (weak) IBOutlet NSWindow *window; 33 | @end 34 | 35 | @implementation AppDelegate 36 | 37 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 38 | // Insert code here to initialize your application 39 | } 40 | 41 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 42 | // Insert code here to tear down your application 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample-Mac/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample-Mac/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2014 Intuit. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample-Mac/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | int main(int argc, const char * argv[]) { 31 | return NSApplicationMain(argc, argv); 32 | } 33 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample.xcodeproj/xcshareddata/xcschemes/GroupedArraySample-Mac.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample.xcodeproj/xcshareddata/xcschemes/GroupedArraySample-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | @interface AppDelegate : UIResponder 31 | 32 | @property (strong, nonatomic) UIWindow *window; 33 | 34 | 35 | @end 36 | 37 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import "AppDelegate.h" 29 | #import "ViewController.h" 30 | 31 | @interface AppDelegate () 32 | 33 | @end 34 | 35 | @implementation AppDelegate 36 | 37 | 38 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 39 | { 40 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 41 | ViewController *viewController = [[ViewController alloc] init]; 42 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; 43 | self.window.rootViewController = navigationController; 44 | [self.window makeKeyAndVisible]; 45 | return YES; 46 | } 47 | 48 | - (void)applicationWillResignActive:(UIApplication *)application 49 | { 50 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 51 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 52 | } 53 | 54 | - (void)applicationDidEnterBackground:(UIApplication *)application 55 | { 56 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 57 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 58 | } 59 | 60 | - (void)applicationWillEnterForeground:(UIApplication *)application 61 | { 62 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 63 | } 64 | 65 | - (void)applicationDidBecomeActive:(UIApplication *)application 66 | { 67 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 68 | } 69 | 70 | - (void)applicationWillTerminate:(UIApplication *)application 71 | { 72 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/INTUFruit.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUFruit.h 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | @interface INTUFruit : NSObject 31 | 32 | @property (nonatomic, strong) NSString *name; 33 | @property (nonatomic, strong) NSString *color; 34 | 35 | + (instancetype)fruitWithName:(NSString *)name color:(NSString *)color; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/INTUFruit.m: -------------------------------------------------------------------------------- 1 | // 2 | // INTUFruit.m 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import "INTUFruit.h" 29 | 30 | @implementation INTUFruit 31 | 32 | + (instancetype)fruitWithName:(NSString *)name color:(NSString *)color 33 | { 34 | INTUFruit *fruit = [self new]; 35 | fruit.name = name; 36 | fruit.color = color; 37 | return fruit; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/INTUFruitCategory.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUFruitCategory.h 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | @interface INTUFruitCategory : NSObject 31 | 32 | @property (nonatomic, strong) NSString *displayName; 33 | 34 | + (instancetype)fruitCategoryWithDisplayName:(NSString *)displayName; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/INTUFruitCategory.m: -------------------------------------------------------------------------------- 1 | // 2 | // INTUFruitCategory.m 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import "INTUFruitCategory.h" 29 | 30 | @implementation INTUFruitCategory 31 | 32 | + (instancetype)fruitCategoryWithDisplayName:(NSString *)displayName 33 | { 34 | INTUFruitCategory *fruitCategory = [self new]; 35 | fruitCategory.displayName = displayName; 36 | return fruitCategory; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | @interface ViewController : UITableViewController 31 | 32 | 33 | @end 34 | 35 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import "ViewController.h" 29 | #import "INTUGroupedArrayImports.h" 30 | #import "INTUFruitCategory.h" 31 | #import "INTUFruit.h" 32 | 33 | @interface ViewController () 34 | 35 | #if __has_feature(objc_generics) 36 | @property (nonatomic, strong) INTUMutableGroupedArray *groupedArray; 37 | #else 38 | @property (nonatomic, strong) INTUMutableGroupedArray *groupedArray; 39 | #endif 40 | 41 | @end 42 | 43 | @implementation ViewController 44 | 45 | - (void)viewDidLoad 46 | { 47 | [super viewDidLoad]; 48 | 49 | self.title = @"INTUGroupedArray Demo"; 50 | 51 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(editTapped:)]; 52 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped:)]; 53 | 54 | [self resetGroupedArray]; 55 | } 56 | 57 | - (void)editTapped:(id)sender 58 | { 59 | [self setEditing:!self.editing animated:YES]; 60 | 61 | UIBarButtonSystemItem leftBarButtonItem = self.editing ? UIBarButtonSystemItemDone : UIBarButtonSystemItemEdit; 62 | [self.navigationItem setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:leftBarButtonItem target:self action:@selector(editTapped:)] animated:YES]; 63 | UIBarButtonSystemItem rightBarButtonItem = self.editing ? UIBarButtonSystemItemTrash : UIBarButtonSystemItemRefresh; 64 | SEL rightBarButtonSelector = self.editing ? @selector(trashTapped:) : @selector(reloadTapped:); 65 | [self.navigationItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:rightBarButtonItem target:self action:rightBarButtonSelector] animated:YES]; 66 | } 67 | 68 | - (void)reloadTapped:(id)sender 69 | { 70 | [self.tableView reloadData]; 71 | } 72 | 73 | - (void)trashTapped:(id)sender 74 | { 75 | [self resetGroupedArray]; 76 | [self.tableView reloadData]; 77 | } 78 | 79 | - (void)resetGroupedArray 80 | { 81 | #if __has_feature(objc_generics) 82 | INTUMutableGroupedArray *groupedArray = [INTUMutableGroupedArray new]; 83 | #else 84 | INTUMutableGroupedArray *groupedArray = [INTUMutableGroupedArray new]; 85 | #endif 86 | 87 | INTUFruitCategory *smallRoundCategory = [INTUFruitCategory fruitCategoryWithDisplayName:@"Small Round"]; 88 | [groupedArray addObject:[INTUFruit fruitWithName:@"Apple" color:@"Red"] toSection:smallRoundCategory]; 89 | [groupedArray addObject:[INTUFruit fruitWithName:@"Orange" color:@"Orange"] toSection:smallRoundCategory]; 90 | [groupedArray addObject:[INTUFruit fruitWithName:@"Lemon" color:@"Yellow"] toSection:smallRoundCategory]; 91 | [groupedArray addObject:[INTUFruit fruitWithName:@"Kiwi" color:@"Green"] toSection:smallRoundCategory]; 92 | 93 | INTUFruitCategory *largeRoundCategory = [INTUFruitCategory fruitCategoryWithDisplayName:@"Large Round"]; 94 | [groupedArray addObject:[INTUFruit fruitWithName:@"Grapefruit" color:@"Pink"] toSection:largeRoundCategory]; 95 | [groupedArray addObject:[INTUFruit fruitWithName:@"Melon" color:@"Brown"] toSection:largeRoundCategory]; 96 | 97 | INTUFruitCategory *vegetableCategory = [INTUFruitCategory fruitCategoryWithDisplayName:@"Vegetable"]; 98 | [groupedArray addObject:[INTUFruit fruitWithName:@"Tomato" color:@"Red"] toSection:vegetableCategory]; 99 | 100 | INTUFruitCategory *otherCategory = [INTUFruitCategory fruitCategoryWithDisplayName:@"Other"]; 101 | [groupedArray addObject:[INTUFruit fruitWithName:@"Banana" color:@"Yellow"] toSection:otherCategory]; 102 | [groupedArray addObject:[INTUFruit fruitWithName:@"Strawberry" color:@"Red"] toSection:otherCategory]; 103 | 104 | self.groupedArray = groupedArray; 105 | } 106 | 107 | 108 | #pragma mark UITableViewDataSource & UITableViewDelegate methods 109 | 110 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 111 | { 112 | return [self.groupedArray countAllSections]; 113 | } 114 | 115 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 116 | { 117 | return [self.groupedArray countObjectsInSectionAtIndex:section]; 118 | } 119 | 120 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 121 | { 122 | return 44.0; 123 | } 124 | 125 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 126 | { 127 | INTUFruit *fruit = [self.groupedArray objectAtIndexPath:indexPath]; 128 | UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil]; 129 | cell.textLabel.text = fruit.name; 130 | cell.detailTextLabel.text = fruit.color; 131 | return cell; 132 | } 133 | 134 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 135 | { 136 | INTUFruitCategory *fruitCategory = [self.groupedArray sectionAtIndex:section]; 137 | return fruitCategory.displayName; 138 | } 139 | 140 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 141 | { 142 | NSUInteger originalSectionCount = [self.groupedArray countAllSections]; 143 | [self.groupedArray moveObjectAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; 144 | if ([self.groupedArray countAllSections] < originalSectionCount) { 145 | // Use dispatch_async to push the section delete animation to the next run loop to avoid visual issues with UITableView 146 | dispatch_async(dispatch_get_main_queue(), ^{ 147 | [tableView deleteSections:[NSIndexSet indexSetWithIndex:sourceIndexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic]; 148 | }); 149 | } 150 | } 151 | 152 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 153 | { 154 | if (editingStyle == UITableViewCellEditingStyleDelete) { 155 | NSUInteger originalSectionCount = [self.groupedArray countAllSections]; 156 | [self.groupedArray removeObjectAtIndexPath:indexPath]; 157 | if ([self.groupedArray countAllSections] < originalSectionCount) { 158 | [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic]; 159 | } else { 160 | [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 161 | } 162 | } 163 | } 164 | 165 | @end 166 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // GroupedArraySample 4 | // https://github.com/intuit/GroupedArray 5 | // 6 | // Copyright (c) 2014-2015 Intuit Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | #import "AppDelegate.h" 30 | 31 | int main(int argc, char * argv[]) { 32 | @autoreleasepool { 33 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArraySampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /GroupedArraySample/GroupedArrayTests-Mac/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /INTUGroupedArray.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "INTUGroupedArray" 3 | s.version = "1.1.5" 4 | s.homepage = "https://github.com/intuit/GroupedArray" 5 | s.license = { :type => 'MIT', :file => 'LICENSE' } 6 | s.author = { "Lucien Dupont" => "lucien_dupont@intuit.com" } 7 | s.source = { :git => "https://github.com/intuit/GroupedArray.git", :tag => "v1.1.5" } 8 | s.source_files = 'Source/INTUGroupedArray/**/*.{h,m}' 9 | s.ios.deployment_target = '6.0' 10 | s.osx.deployment_target = '10.7' 11 | s.watchos.deployment_target = '2.0' 12 | s.tvos.deployment_target = '9.0' 13 | s.requires_arc = true 14 | s.summary = "An Objective-C and Swift collection for iOS and OS X that stores objects grouped into sections." 15 | s.description = <<-DESC 16 | INTUGroupedArray is an Objective-C data structure that takes the common one-dimensional array to the next dimension. The grouped array is designed with a familiar API to fit right in alongside Foundation collections like NSArray, with fully-featured immutable and mutable variants. A thin bridge brings the grouped array to Swift as native classes, where it harnesses the power, safety, and flexibility of generics, optionals, subscripts, literals, tuples, and much more. 17 | 18 | INTUGroupedArray is extremely versatile, and can replace complicated nested arrays or combinations of other data structures as a general purpose data storage mechanism. The grouped array is ideal to use as a UITableView data source, as it is highly compatible with the data source and delegate callbacks -- requiring only a single line of code in many cases. However, it is suitable for use across the entire stack of iOS and OS X applications. 19 | DESC 20 | end 21 | -------------------------------------------------------------------------------- /Images/INTUGroupedArray-Illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuit/GroupedArray/ed3c4fdf0cd579ef245134cd368005e6440e5516/Images/INTUGroupedArray-Illustration.png -------------------------------------------------------------------------------- /Images/INTUGroupedArray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intuit/GroupedArray/ed3c4fdf0cd579ef245134cd368005e6440e5516/Images/INTUGroupedArray.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Intuit Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [![INTUGroupedArray](https://github.com/intuit/GroupedArray/blob/master/Images/INTUGroupedArray.png?raw=true)](#) 2 | [![Build Status](http://img.shields.io/travis/intuit/GroupedArray.svg?style=flat)](https://travis-ci.org/intuit/GroupedArray) [![Test Coverage](http://img.shields.io/coveralls/intuit/GroupedArray.svg?style=flat)](https://coveralls.io/r/intuit/GroupedArray) [![Version](http://img.shields.io/cocoapods/v/INTUGroupedArray.svg?style=flat)](http://cocoapods.org/pods/INTUGroupedArray) [![Platform](http://img.shields.io/cocoapods/p/INTUGroupedArray.svg?style=flat)](http://cocoapods.org/pods/INTUGroupedArray) [![License](http://img.shields.io/cocoapods/l/INTUGroupedArray.svg?style=flat)](LICENSE) 3 | 4 | An Objective-C and Swift collection for iOS and OS X that stores objects grouped into sections. 5 | 6 | INTUGroupedArray is an Objective-C data structure that takes the common one-dimensional array to the next dimension. The grouped array is designed with a familiar API to fit right in alongside Foundation collections like NSArray, with fully-featured immutable and mutable variants. A thin bridge brings the grouped array to Swift as native classes, where it harnesses the power, safety, and flexibility of generics, optionals, subscripts, literals, tuples, and much more. 7 | 8 | INTUGroupedArray is extremely versatile, and can replace complicated nested arrays or combinations of other data structures as a general purpose data storage mechanism. The grouped array is ideal to use as a UITableView data source, as it is highly compatible with the data source and delegate callbacks -- requiring only a single line of code in many cases. However, it is suitable for use across the entire stack of iOS and OS X applications. 9 | 10 | ## Setup 11 | 12 | ### Objective-C 13 | 14 | #### Using [CocoaPods](http://cocoapods.org) 15 | 16 | 1. Add the pod `INTUGroupedArray` to your [Podfile](http://guides.cocoapods.org/using/the-podfile.html). 17 | 18 | pod 'INTUGroupedArray' 19 | 20 | 2. Run `pod install` from Terminal, then open your app's `.xcworkspace` file to launch Xcode. 21 | 3. Import the umbrella header `INTUGroupedArrayImports.h` so that all the grouped array classes are available to use. Typically, this should be written as `#import ` 22 | 23 | #### Manually from GitHub 24 | 25 | 1. Download the Objective-C source files in the [Source/INTUGroupedArray directory](Source/INTUGroupedArray). 26 | 2. Add all the files to your Xcode project (drag and drop is easiest). 27 | 3. Import the umbrella header `INTUGroupedArrayImports.h` so that all the grouped array classes are available to use. 28 | 29 | ### Swift 30 | 31 | 1. To use the grouped array in Swift, you first need to obtain the Objective-C source code and integrate it into your project (see above). 32 | 2. Download the `GroupedArray.swift` Swift source file in the [Source/Swift directory](Source/Swift) and add it to your project. 33 | 3. If you haven't already, you will need to set up an [Objective-C Bridging Header](https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html) for your project. 34 | 4. Add the umbrella header `INTUGroupedArrayImports.h` to your Bridging Header file. (The Swift grouped array classes depend on this so they can access the Objective-C source.) 35 | 36 | At this point, the native Swift classes `GroupedArray` and `MutableGroupedArray` will be available to use in your project's Swift code. 37 | 38 | ## Concept 39 | At a high level, the grouped array is a collection of sections, each of which contains an array of objects. Both the sections and objects can be instances of any class. 40 | 41 | [![INTUGroupedArray Illustration](https://github.com/intuit/GroupedArray/blob/master/Images/INTUGroupedArray-Illustration.png?raw=true)](#) 42 | 43 | The grouped array closely mirrors the structure of a UITableView, which also has rows organized into one or more sections. As a result, the table view data source and delegate methods map directly to grouped array API methods. 44 | 45 | Some of the grouped array APIs are similar to those of a dictionary (e.g. retrieving the objects in a section by passing in the section itself, instead of its index). *Note that, unlike a dictionary, the grouped array does not require elements (sections or objects) to implement a `-hash` method or require that elements have a stable hash value once inside a grouped array. However, this comes at the cost of performance for certain operations like section lookups.* 46 | 47 | Like the Foundation collections, there are two variants of the grouped array: 48 | 49 | * `INTUGroupedArray`: An immutable grouped array. Thread safe. (Swift type: `GroupedArray`) 50 | * `INTUMutableGroupedArray`: A mutable subclass. Not thread safe. (Swift type: `MutableGroupedArray`) 51 | 52 | As a best practice, you should favor the static immutable grouped array over the dynamic mutable variant. 53 | 54 | The performance of most operations is annotated in the detailed documentation above the implementation of each method in the .m file. (The documentation in the header files is abbreviated to provide a convenient API reference.) 55 | 56 | ### Key Design Points 57 | 58 | #### No empty sections 59 | Every section in a grouped array must contain at least one object, similar to how every key in a dictionary must be associated with a value. This design simplifies some of the complexity when dealing with multi-dimensional data structures. For example, this allows a section to be removed as soon as its last object is removed. If there is a use case where you do want an 'empty' section, you can use a single placeholder object to achieve this. 60 | 61 | #### Sections should be unique 62 | The grouped array is designed under the assumption that no two sections in the grouped array are considered equal (using `-isEqual:`). For example, this allows sections to be automatically created and removed as needed in the mutable grouped array. Although it is discouraged, it is technically possible to have duplicate sections, however this impairs the functionality of section-based APIs (e.g. `-indexOfSection:`, `-indexOfObject:inSection:`, `-addObject:toSection:`, and others) which will only operate on one instance of the section. 63 | 64 | #### Errors are handled gracefully 65 | Most errors are not fatal errors, and there is a better way to recover than terminating the entire app. In Debug builds, the grouped array will throw exceptions on errors, however with assertions disabled in Release builds, it will not crash. For example, attempting to access a section at an out-of-bounds index will throw an exception in Debug, but will simply return nil in Release. In the Swift grouped array, this is communicated via implicitly unwrapped optionals -- these values will only be nil if there was an error, so you should test for nil if you wish to avoid runtime errors. 66 | 67 | ## Usage 68 | 69 | Here are some snippets of common operations with a grouped array. Note that this is only a small portion of the overall API -- refer to the source files for the full API and documentation. 70 | 71 | ### Objective-C 72 | 73 | Create an immutable grouped array using the literal syntax: 74 | 75 | INTUGroupedArray *groupedArray = [INTUGroupedArray literal:@[@"Section 1", @[@"Object A", @"Object B"], 76 | @"Section 2", @[@"Object C"], 77 | @"Section 3", @[@"Object D", @"Object E", @"Object F"]]]; 78 | 79 | Count the number of sections and objects in the grouped array: 80 | 81 | NSUInteger sectionCount = [groupedArray countAllSections]; 82 | NSUInteger objectCount = [groupedArray countAllObjects]; 83 | 84 | Access the first section: 85 | 86 | id section = [groupedArray sectionAtIndex:0]; 87 | 88 | Get an array of objects in the first section: 89 | 90 | NSArray *objectsInSection = [groupedArray objectsInSectionAtIndex:0]; 91 | 92 | Access the first object in the first section (using NSIndexPath): 93 | 94 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; 95 | id object = [groupedArray objectAtIndexPath:indexPath]; 96 | 97 | Get an array of all objects: 98 | 99 | NSArray *allObjects = [groupedArray allObjects]; 100 | 101 | Iterate over the objects in the grouped array: 102 | 103 | for (id object in groupedArray) { /* do something with object */ } 104 | 105 | Get a mutable copy of the immutable grouped array: 106 | 107 | INTUMutableGroupedArray *mutableGroupedArray = [groupedArray mutableCopy]; 108 | 109 | Add an object to the end of the first section: 110 | 111 | [mutableGroupedArray addObject:@"New Object" toSectionAtIndex:0]; 112 | 113 | Insert an object at the beginning of Section 3: 114 | 115 | [mutableGroupedArray insertObject:@"Another Object" atIndex:0 inSection:@"Section 3"]; 116 | 117 | Remove the second section: 118 | 119 | [mutableGroupedArray removeSectionAtIndex:1]; 120 | 121 | Test if two grouped arrays are equal (contain the same sections & objects): 122 | 123 | if ([groupedArray isEqual:mutableGroupedArray]) { /* the two grouped arrays are equal */ } 124 | 125 | ### Swift 126 | 127 | Create an immutable grouped array (with both sections and objects of type NSString) using an array literal: 128 | 129 | let groupedArray: GroupedArray = ["Section 1", ["Object A", "Object B"], 130 | "Section 2", ["Object C"], 131 | "Section 3", ["Object D", "Object E", "Object F"]] 132 | 133 | Count the number of sections and objects: 134 | 135 | let sectionCount = groupedArray.countAllSections() 136 | let objectCount = groupedArray.countAllObjects() 137 | 138 | Access the first section using a subscript: 139 | 140 | let section = groupedArray[0] 141 | 142 | Access the first object in the second section using a subscript: 143 | 144 | let object = groupedArray[1, 0] 145 | 146 | Access an object by index path using a subscript: 147 | 148 | let indexPath = NSIndexPath(forRow: 0, inSection: 0) 149 | let object = groupedArray[indexPath] 150 | 151 | Get the last object in the grouped array, if there is one: 152 | 153 | if let lastObject = groupedArray.lastObject() { /* do something with lastObject */ } 154 | 155 | Iterate over the grouped array: 156 | 157 | for (section, object) in groupedArray { /* do something with the section/object */ } 158 | 159 | Get a mutable copy of the immutable grouped array: 160 | 161 | var mutableGroupedArray = groupedArray.mutableCopy() // the type of mutableGroupedArray is inferred to be: MutableGroupedArray 162 | 163 | Replace the first object in the third section using a subscript: 164 | 165 | mutableGroupedArray[2, 0] = "Another Object" 166 | 167 | Test if two grouped arrays are equal (contain the same sections & objects): 168 | 169 | if (groupedArray == mutableGroupedArray) { /* the two grouped arrays are equal */ } 170 | 171 | ## Sample Projects 172 | There are two sample projects provided. 173 | 174 | The [Objective-C sample project](GroupedArraySample) demonstrates how the grouped array can be used to back a table view on iOS. This project requires Xcode 6.0 or higher. 175 | 176 | The [Swift sample project](SwiftGroupedArray) highlights some additional capabilities available when using the Swift interface. This project requires Xcode 6.1 or higher. 177 | 178 | ### Unit Tests 179 | The unit test suite is incorporated into the Objective-C sample project. It is cross platform, and can run on iOS and OS X. 180 | 181 | ## Issues & Contributions 182 | Please [open an issue here on GitHub](https://github.com/intuit/GroupedArray/issues/new) if you have a problem, suggestion, or other comment. 183 | 184 | Pull requests are welcome and encouraged! There are no official guidelines, but please try to be consistent with the existing code style. Any contributions should include new or updated unit tests as necessary to maintain thorough test coverage. 185 | 186 | ## License 187 | INTUGroupedArray is provided under the MIT license. 188 | 189 | # INTU on GitHub 190 | Check out more [iOS and OS X open source projects from Intuit](https://github.com/search?utf8=✓&q=user%3Aintuit+language%3Aobjective-c&type=Repositories&ref=searchresults)! 191 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/INTUGroupedArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUGroupedArray.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import "INTUGroupedArrayDefines.h" 29 | 30 | GA__INTU_ASSUME_NONNULL_BEGIN 31 | 32 | 33 | #pragma mark - INTUGroupedArraySectionEnumerator 34 | 35 | /** A protocol for an enumerator that will enumerate over sections in a grouped array. */ 36 | @protocol INTUGroupedArraySectionEnumerator 37 | 38 | /** Returns the next section from the grouped array being enumerated. Returns nil when all sections have been enumerated. */ 39 | - (GA__INTU_NULLABLE id)nextSection; 40 | 41 | /** Returns an array of all sections from the grouped array that have not yet been enumerated. */ 42 | - (NSArray *)allSections; 43 | 44 | @end 45 | 46 | 47 | #pragma mark - INTUGroupedArray 48 | 49 | /** 50 | A collection that holds an array of sections and each section contains an array of objects. Both sections 51 | and objects can be instances of any class. INTUGroupedArray works particularly well as a backing store for 52 | a table view. 53 | 54 | INTUGroupedArray guarantees that there will never be empty sections - all sections will contain at least 55 | one object. 56 | 57 | Instances of INTUGroupedArray are thread safe, however subclasses may not be (for instance, 58 | INTUMutableGroupedArray is NOT thread safe). 59 | 60 | Regular INTUGroupedArray instances act like standard collections and will raise exceptions if assertions are 61 | enabled when attempting to access sections or objects that don't exist in the grouped array. However, if 62 | assertions are disabled, INTUGroupedArray will fail gracefully. 63 | */ 64 | @interface GA__INTU_GENERICS(INTUGroupedArray, SectionType, ObjectType) : NSObject 65 | { 66 | @protected 67 | /** A token that is incremented on every mutation. */ 68 | unsigned long _mutations; 69 | } 70 | 71 | /** Helper method to create an index path when the UIKit category on NSIndexPath is not available. */ 72 | + (NSIndexPath *)indexPathForRow:(NSUInteger)row inSection:(NSUInteger)section; 73 | 74 | 75 | #pragma mark Class Factory Methods 76 | 77 | /** Creates and returns a new empty grouped array. */ 78 | + (instancetype)groupedArray; 79 | /** Creates and returns a new grouped array with a single section ([NSObject new]) containing the objects in the array. */ 80 | + (instancetype)groupedArrayWithArray:(GA__INTU_NULLABLE NSArray *)array; 81 | 82 | /** Creates and returns a grouped array from the literal syntax. 83 | Syntax: @[section1, @[object1A, object1B, ...], section2, @[object2A, object2B, ...], ...] */ 84 | + (instancetype)literal:(NSArray *)groupedArrayLiteral; 85 | 86 | 87 | #pragma mark Initializers 88 | 89 | /** Creates and returns a new grouped array with the contents of a given grouped array. */ 90 | - (instancetype)initWithGroupedArray:(INTUGroupedArray *)groupedArray; 91 | /** Creates and returns a new grouped array with the contents of a given grouped array, optionally copying the sections & objects. */ 92 | - (instancetype)initWithGroupedArray:(INTUGroupedArray *)groupedArray copyItems:(BOOL)copyItems; 93 | 94 | 95 | #pragma mark Access Methods 96 | 97 | /** Returns the section at the index. */ 98 | - (GA__INTU_GENERICS_TYPE(SectionType))sectionAtIndex:(NSUInteger)index; 99 | /** Returns the number of sections. */ 100 | - (NSUInteger)countAllSections; 101 | /** Returns an array of all the sections. */ 102 | - (GA__INTU_GENERICS(NSArray, SectionType) *)allSections; 103 | /** Returns whether the section exists. */ 104 | - (BOOL)containsSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 105 | /** Returns the index for the section. */ 106 | - (NSUInteger)indexOfSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 107 | 108 | /** Returns the object at the index in the section. */ 109 | - (GA__INTU_GENERICS_TYPE(ObjectType))objectAtIndex:(NSUInteger)index inSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 110 | /** Returns the object at the index path. */ 111 | - (GA__INTU_GENERICS_TYPE(ObjectType))objectAtIndexPath:(NSIndexPath *)indexPath; 112 | /** Returns the first object in the first section. */ 113 | - (GA__INTU_NULLABLE GA__INTU_GENERICS_TYPE(ObjectType))firstObject; 114 | /** Returns the last object in the last section. */ 115 | - (GA__INTU_NULLABLE GA__INTU_GENERICS_TYPE(ObjectType))lastObject; 116 | /** Returns whether the object exists in any section. */ 117 | - (BOOL)containsObject:(GA__INTU_GENERICS_TYPE(ObjectType))object; 118 | /** Returns the index path of the first instance of the object across all sections. */ 119 | - (NSIndexPath *)indexPathOfObject:(GA__INTU_GENERICS_TYPE(ObjectType))object; 120 | /** Returns whether the object exists in the section. */ 121 | - (BOOL)containsObject:(GA__INTU_GENERICS_TYPE(ObjectType))object inSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 122 | /** Returns the index of the first instance of the object in the section. */ 123 | - (NSUInteger)indexOfObject:(GA__INTU_GENERICS_TYPE(ObjectType))object inSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 124 | /** Returns the number of objects in the section. */ 125 | - (NSUInteger)countObjectsInSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 126 | /** Returns the number of objects in the section at the index. */ 127 | - (NSUInteger)countObjectsInSectionAtIndex:(NSUInteger)index; 128 | /** Returns the objects in the section. */ 129 | - (GA__INTU_GENERICS(NSArray, ObjectType) *)objectsInSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 130 | /** Returns the objects in the section at the index. */ 131 | - (GA__INTU_GENERICS(NSArray, ObjectType) *)objectsInSectionAtIndex:(NSUInteger)index; 132 | /** Returns the total number of objects in all sections. */ 133 | - (NSUInteger)countAllObjects; 134 | /** Returns an array of all objects in all sections. */ 135 | - (GA__INTU_GENERICS(NSArray, ObjectType) *)allObjects; 136 | 137 | /** Executes the block for each section in the grouped array. */ 138 | - (void)enumerateSectionsUsingBlock:(void (^)(GA__INTU_GENERICS_TYPE(SectionType) section, NSUInteger index, BOOL *stop))block; 139 | /** Executes the block for each section in the grouped array with the specified enumeration options. */ 140 | - (void)enumerateSectionsWithOptions:(NSEnumerationOptions)options usingBlock:(void (^)(GA__INTU_GENERICS_TYPE(SectionType) section, NSUInteger index, BOOL *stop))block; 141 | /** Executes the block for each object in the grouped array. */ 142 | - (void)enumerateObjectsUsingBlock:(void (^)(GA__INTU_GENERICS_TYPE(ObjectType) object, NSIndexPath *indexPath, BOOL *stop))block; 143 | /** Executes the block for each object in the grouped array with the specified enumeration options. */ 144 | - (void)enumerateObjectsWithOptions:(NSEnumerationOptions)options usingBlock:(void (^)(GA__INTU_GENERICS_TYPE(ObjectType) object, NSIndexPath *indexPath, BOOL *stop))block; 145 | /** Executes the block for each object in the section at the index. */ 146 | - (void)enumerateObjectsInSectionAtIndex:(NSUInteger)sectionIndex usingBlock:(void (^)(GA__INTU_GENERICS_TYPE(ObjectType) object, NSIndexPath *indexPath, BOOL *stop))block; 147 | /** Executes the block for each object in the section at the index with the specified enumeration options. */ 148 | - (void)enumerateObjectsInSectionAtIndex:(NSUInteger)sectionIndex withOptions:(NSEnumerationOptions)options usingBlock:(void (^)(GA__INTU_GENERICS_TYPE(ObjectType) object, NSIndexPath *indexPath, BOOL *stop))block; 149 | 150 | /** Returns an enumerator that will access each section in the grouped array, starting with the first section. */ 151 | - (NSEnumerator *)sectionEnumerator; 152 | /** Returns an enumerator that will access each section in the grouped array, starting with the last section. */ 153 | - (NSEnumerator *)reverseSectionEnumerator; 154 | /** Returns an enumerator that will access each object in the grouped array, starting with the first section. */ 155 | - (NSEnumerator *)objectEnumerator; 156 | /** Returns an enumerator that will access each object in the grouped array, starting with the last section. */ 157 | - (NSEnumerator *)reverseObjectEnumerator; 158 | 159 | /** Returns the index of the first section in the grouped array that passes the test. */ 160 | - (NSUInteger)indexOfSectionPassingTest:(BOOL (^)(GA__INTU_GENERICS_TYPE(SectionType) section, NSUInteger index, BOOL *stop))block; 161 | /** Returns the index path of the first object in the grouped array that passes the test. */ 162 | - (NSIndexPath *)indexPathOfObjectPassingTest:(BOOL (^)(GA__INTU_GENERICS_TYPE(ObjectType) object, NSIndexPath *indexPath, BOOL *stop))block; 163 | 164 | /** Returns whether the contents of this grouped array are equal to the contents of another grouped array. */ 165 | - (BOOL)isEqualToGroupedArray:(GA__INTU_NULLABLE INTUGroupedArray *)otherGroupedArray; 166 | 167 | /** Returns a new grouped array filtered by evaluating the section & object predicates against all sections & objects and removing those that do not match. Empty sections will be removed. */ 168 | - (GA__INTU_GENERICS(INTUGroupedArray, SectionType, ObjectType) *)filteredGroupedArrayUsingSectionPredicate:(GA__INTU_NULLABLE NSPredicate *)sectionPredicate objectPredicate:(GA__INTU_NULLABLE NSPredicate *)objectPredicate; 169 | 170 | /** Returns a new grouped array with the sections sorted using the section comparator, and the objects in each section sorted using the object comparator. */ 171 | - (GA__INTU_GENERICS(INTUGroupedArray, SectionType, ObjectType) *)sortedGroupedArrayUsingSectionComparator:(GA__INTU_NULLABLE NSComparator)sectionCmptr objectComparator:(GA__INTU_NULLABLE NSComparator)objectCmptr; 172 | 173 | @end 174 | 175 | GA__INTU_ASSUME_NONNULL_END 176 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/INTUGroupedArrayImports.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUGroupedArrayImports.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | /** 28 | This umbrella header file imports all of the INTUGroupedArray headers, so that all the classes are available for use. 29 | */ 30 | 31 | #ifndef INTUGroupedArrayImports_h 32 | #define INTUGroupedArrayImports_h 33 | 34 | #import "INTUGroupedArray.h" 35 | #import "INTUMutableGroupedArray.h" 36 | 37 | #endif /* INTUGroupedArrayImports_h */ 38 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/INTUMutableGroupedArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUMutableGroupedArray.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import "INTUGroupedArray.h" 28 | 29 | GA__INTU_ASSUME_NONNULL_BEGIN 30 | 31 | 32 | /** 33 | A mutable subclass of INTUGroupedArray. 34 | 35 | INTUMutableGroupedArray is NOT thread-safe (just like NSMutableArray). It should only be accessed from one thread. 36 | 37 | Calling -[copy] will return an immutable instance of INTUGroupedArray. 38 | */ 39 | @interface GA__INTU_GENERICS(INTUMutableGroupedArray, SectionType, ObjectType) : INTUGroupedArray 40 | 41 | #pragma mark Adding 42 | 43 | /** Adds an object to the section. If the section does not exist, it will be created. */ 44 | - (void)addObject:(GA__INTU_GENERICS_TYPE(ObjectType))object toSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 45 | /** Adds an object to the section, using the section index hint to attempt to quickly locate the section. If the section does not exist, it will be created. */ 46 | - (void)addObject:(GA__INTU_GENERICS_TYPE(ObjectType))object toSection:(GA__INTU_GENERICS_TYPE(SectionType))section withSectionIndexHint:(NSUInteger)sectionIndexHint; 47 | /** Adds an object to an existing section at the index. */ 48 | - (void)addObject:(GA__INTU_GENERICS_TYPE(ObjectType))object toSectionAtIndex:(NSUInteger)index; 49 | /** Adds the objects in the array to the section. If the section does not exist, it will be created. */ 50 | - (void)addObjectsFromArray:(GA__INTU_NULLABLE GA__INTU_GENERICS(NSArray, ObjectType) *)array toSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 51 | 52 | #pragma mark Inserting 53 | 54 | /** Inserts the object at the index in the section. If the section does not exist, it will be created. */ 55 | - (void)insertObject:(GA__INTU_GENERICS_TYPE(ObjectType))object atIndex:(NSUInteger)index inSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 56 | /** Inserts the object at the index path. The index path must correspond to an existing section. */ 57 | - (void)insertObject:(GA__INTU_GENERICS_TYPE(ObjectType))object atIndexPath:(NSIndexPath *)indexPath; 58 | 59 | #pragma mark Replacing 60 | 61 | /** Replaces the section at the index with another section. */ 62 | - (void)replaceSectionAtIndex:(NSUInteger)index withSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 63 | /** Replaces the object at the index path with another object. */ 64 | - (void)replaceObjectAtIndexPath:(NSIndexPath *)indexPath withObject:(GA__INTU_GENERICS_TYPE(ObjectType))object; 65 | 66 | #pragma mark Moving 67 | 68 | /** Moves the section at the index to a new index. */ 69 | - (void)moveSectionAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex; 70 | /** Moves the object at the index path to a new index path. The new index path must correspond to an existing section. */ 71 | - (void)moveObjectAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath; 72 | 73 | #pragma mark Exchanging 74 | 75 | /** Exchanges the section at one index with a section at another index. */ 76 | - (void)exchangeSectionAtIndex:(NSUInteger)index1 withSectionAtIndex:(NSUInteger)index2; 77 | /** Exchanges the object at one index path with an object at another index path. */ 78 | - (void)exchangeObjectAtIndexPath:(NSIndexPath *)indexPath1 withObjectAtIndexPath:(NSIndexPath *)indexPath2; 79 | 80 | #pragma mark Removing 81 | 82 | /** Removes all objects and sections. */ 83 | - (void)removeAllObjects; 84 | /** Removes the section and all objects in it. */ 85 | - (void)removeSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 86 | /** Removes the section at the index and all objects in it. */ 87 | - (void)removeSectionAtIndex:(NSUInteger)index; 88 | /** Removes all occurrences of the object across all sections. Empty sections will be removed. */ 89 | - (void)removeObject:(GA__INTU_GENERICS_TYPE(ObjectType))object; 90 | /** Removes all occurrences of the object from the section. Empty sections will be removed. */ 91 | - (void)removeObject:(GA__INTU_GENERICS_TYPE(ObjectType))object fromSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 92 | /** Removes the object at the index from the section. Empty sections will be removed. */ 93 | - (void)removeObjectAtIndex:(NSUInteger)index fromSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 94 | /** Removes the object at the index path. Empty sections will be removed. */ 95 | - (void)removeObjectAtIndexPath:(NSIndexPath *)indexPath; 96 | 97 | #pragma mark Filtering 98 | 99 | /** Evaluates the section & object predicates against all sections & objects and removes those that do not match. Empty sections will be removed. */ 100 | - (void)filterUsingSectionPredicate:(GA__INTU_NULLABLE NSPredicate *)sectionPredicate objectPredicate:(GA__INTU_NULLABLE NSPredicate *)objectPredicate; 101 | 102 | #pragma mark Sorting 103 | 104 | /** Sorts the sections using the section comparator, and the objects in each section using the object comparator. */ 105 | - (void)sortUsingSectionComparator:(GA__INTU_NULLABLE NSComparator)sectionCmptr objectComparator:(GA__INTU_NULLABLE NSComparator)objectCmptr; 106 | 107 | @end 108 | 109 | GA__INTU_ASSUME_NONNULL_END 110 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/INTUMutableGroupedArray.m: -------------------------------------------------------------------------------- 1 | // 2 | // INTUMutableGroupedArray.m 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import "INTUMutableGroupedArray.h" 28 | #import "INTUGroupedArraySectionContainer.h" 29 | #import "INTUGroupedArrayInternal.h" 30 | 31 | @interface GA__INTU_GENERICS(INTUMutableGroupedArray, SectionType, ObjectType) () 32 | 33 | // A mutable array of INTUMutableGroupedArraySectionContainer objects, which serves as the backing store for the grouped array. 34 | // Note that this property does not have its own backing instance variable; it uses the superclass sectionContainers property for storage. 35 | @property (nonatomic) GA__INTU_GENERICS(NSMutableArray, GA__INTU_GENERICS(INTUMutableGroupedArraySectionContainer, SectionType, ObjectType) *) *mutableSectionContainers; 36 | 37 | @end 38 | 39 | @implementation INTUMutableGroupedArray 40 | 41 | 42 | #pragma mark Overrides 43 | 44 | + (instancetype)groupedArrayWithArray:(NSArray *)array 45 | { 46 | INTUMutableGroupedArray *groupedArray = [self new]; 47 | if ([array count] > 0) { 48 | INTUMutableGroupedArraySectionContainer *sectionContainer = [INTUMutableGroupedArraySectionContainer sectionContainerWithSection:[NSObject new]]; 49 | sectionContainer.mutableObjects = [array mutableCopy]; 50 | groupedArray.mutableSectionContainers = [NSMutableArray arrayWithObject:sectionContainer]; 51 | } 52 | return groupedArray; 53 | } 54 | 55 | - (instancetype)init 56 | { 57 | self = [super init]; 58 | if (self) { 59 | self.mutableSectionContainers = [NSMutableArray new]; 60 | } 61 | return self; 62 | } 63 | 64 | - (id)copyWithZone:(NSZone *)zone 65 | { 66 | INTUGroupedArray *copy = [[INTUGroupedArray allocWithZone:zone] init]; 67 | // The array of sectionContainers is deep copied so that the INTUGroupedArraySectionContainer objects it holds are also copied, 68 | // but since this is only a one level deep copy, the sections & objects in the grouped array will NOT be deep copied! 69 | copy.sectionContainers = [[NSMutableArray allocWithZone:zone] initWithArray:self.sectionContainers copyItems:YES]; 70 | return copy; 71 | } 72 | 73 | - (id)mutableCopyWithZone:(NSZone *)zone 74 | { 75 | __typeof(self) copy = [[[self class] allocWithZone:zone] init]; 76 | for (INTUGroupedArraySectionContainer *sectionContainer in self.sectionContainers) { 77 | [copy.mutableSectionContainers addObject:[sectionContainer mutableCopy]]; 78 | } 79 | return copy; 80 | } 81 | 82 | - (NSMutableArray *)mutableSectionContainers 83 | { 84 | return (NSMutableArray *)[super sectionContainers]; 85 | } 86 | 87 | - (void)setMutableSectionContainers:(NSMutableArray *)mutableSectionContainers 88 | { 89 | [super setSectionContainers:mutableSectionContainers]; 90 | } 91 | 92 | 93 | #pragma mark Adding 94 | 95 | /** 96 | Adds an object to the section. If the section does not exist, it will be created. 97 | Performance: O(n), where n is the number of sections 98 | 99 | @param object The object to add to the grouped array. 100 | @param section The section to add the object to. 101 | */ 102 | - (void)addObject:(id)object toSection:(id)section 103 | { 104 | [self addObject:object toSection:section withSectionIndexHint:NSNotFound]; 105 | } 106 | 107 | /** 108 | Adds an object to the section, using the section index hint to attempt to quickly locate the section. If the section does 109 | not exist, it will be created. Passing an accurate hint for the section index will dramatically accelerate performance 110 | when there are a large number of sections, as it will avoid having to call -[self indexOfSection:] to find the section. 111 | Performance: O(1) assuming an accurate section index hint; otherwise O(n), where n is the number of sections 112 | 113 | @param object The object to add to the grouped array. 114 | @param section The section to add the object to. 115 | @param sectionIndexHint An optional hint to the index of the section. (Pass NSNotFound to ignore the hint.) 116 | */ 117 | - (void)addObject:(id)object toSection:(id)section withSectionIndexHint:(NSUInteger)sectionIndexHint 118 | { 119 | if (!object || !section) { 120 | NSAssert(object, @"Object should not be nil."); 121 | NSAssert(section, @"Section should not be nil."); 122 | return; 123 | } 124 | 125 | NSMutableArray *objectsArray = [self _objectsArrayForSection:section withSectionIndexHint:sectionIndexHint]; 126 | if (objectsArray == nil) { 127 | // Section does not exist yet, we need to create it 128 | INTUMutableGroupedArraySectionContainer *sectionContainer = [INTUMutableGroupedArraySectionContainer sectionContainerWithSection:section]; 129 | [self.mutableSectionContainers addObject:sectionContainer]; 130 | objectsArray = sectionContainer.mutableObjects; 131 | } 132 | 133 | [objectsArray addObject:object]; 134 | _mutations++; 135 | } 136 | 137 | /** 138 | Adds an object to an existing section at the index. 139 | An exception will be raised if the index is out of bounds. 140 | Performance: O(1) 141 | 142 | @param object The object to add to the grouped array. 143 | @param index The index of the section to add the object to. 144 | */ 145 | - (void)addObject:(id)object toSectionAtIndex:(NSUInteger)index 146 | { 147 | if (index >= [self countAllSections]) { 148 | NSAssert(index < [self countAllSections], @"Index out of bounds!"); 149 | return; 150 | } 151 | 152 | INTUMutableGroupedArraySectionContainer *sectionContainer = self.sectionContainers[index]; 153 | NSMutableArray *objectsArray = sectionContainer.mutableObjects; 154 | [objectsArray addObject:object]; 155 | _mutations++; 156 | } 157 | 158 | /** 159 | Adds the objects in the array to the section. If the section does not exist, it will be created. 160 | If the array is nil or empty, this method will do nothing. 161 | Performance: O(n+m), where n is the number of sections, and m is the number of objects to add 162 | 163 | @param array The array of objects to add to the grouped array. 164 | @param section The section to add the objects to. 165 | */ 166 | - (void)addObjectsFromArray:(NSArray *)array toSection:(id)section 167 | { 168 | NSUInteger sectionIndex = [self indexOfSection:section]; 169 | for (id obj in array) { 170 | [self addObject:obj toSection:section withSectionIndexHint:sectionIndex]; 171 | } 172 | } 173 | 174 | #pragma mark Inserting 175 | 176 | /** 177 | Inserts the object at the index in the section. If the section does not exist, it will be created. 178 | Performance: O(n+m), where n is the number of sections, and m is the number of objects in the section 179 | 180 | @param object The object to add to the grouped array. 181 | @param index The index to insert the object at in the section. Must not be greater than the current number 182 | of objects in the section. 183 | @param section The section to add the object to. 184 | */ 185 | - (void)insertObject:(id)object atIndex:(NSUInteger)index inSection:(id)section 186 | { 187 | if (!object || !section) { 188 | NSAssert(object, @"Object should not be nil."); 189 | NSAssert(section, @"Section should not be nil."); 190 | return; 191 | } 192 | 193 | NSMutableArray *objectsArray = [self _objectsArrayForSection:section withSectionIndexHint:NSNotFound]; 194 | if (objectsArray == nil) { 195 | // Section does not exist yet, we need to create it 196 | INTUMutableGroupedArraySectionContainer *sectionContainer = [INTUMutableGroupedArraySectionContainer sectionContainerWithSection:section]; 197 | [self.mutableSectionContainers addObject:sectionContainer]; 198 | objectsArray = sectionContainer.mutableObjects; 199 | } 200 | 201 | if (index > [objectsArray count]) { 202 | NSAssert(index <= [objectsArray count], @"Index out of bounds!"); 203 | return; 204 | } 205 | 206 | [objectsArray insertObject:object atIndex:index]; 207 | _mutations++; 208 | } 209 | 210 | /** 211 | Inserts the object at the index path. The index path must correspond to an existing section. 212 | Performance: O(n), where n is the number of objects in the section 213 | 214 | @param object The object to add to the grouped array. 215 | @param indexPath The index path to insert the object at. The section component must be less than the current number of sections, 216 | and the row component must not be greater than the current number of objects in the section. 217 | */ 218 | - (void)insertObject:(id)object atIndexPath:(NSIndexPath *)indexPath 219 | { 220 | if (!object || !indexPath) { 221 | NSAssert(object, @"Object should not be nil."); 222 | NSAssert(indexPath, @"Index path should not be nil."); 223 | return; 224 | } 225 | 226 | NSUInteger sectionIndex = [indexPath indexAtPosition:0]; 227 | NSUInteger objectIndex = [indexPath indexAtPosition:1]; 228 | 229 | if (sectionIndex >= [self countAllSections]) { 230 | NSAssert(sectionIndex < [self countAllSections], @"Section index out of bounds!"); 231 | return; 232 | } 233 | 234 | INTUMutableGroupedArraySectionContainer *sectionContainer = self.sectionContainers[sectionIndex]; 235 | NSMutableArray *objectsArray = sectionContainer.mutableObjects; 236 | 237 | if (objectIndex > [objectsArray count]) { 238 | NSAssert(objectIndex <= [objectsArray count], @"Object index out of bounds!"); 239 | return; 240 | } 241 | 242 | [objectsArray insertObject:object atIndex:objectIndex]; 243 | _mutations++; 244 | } 245 | 246 | #pragma mark Replacing 247 | 248 | /** 249 | Replaces the section at the index with another section. 250 | Performance: O(1) 251 | 252 | @param index The index of the section to replace. 253 | @param section The section with which to replace the existing section at the given index. 254 | */ 255 | - (void)replaceSectionAtIndex:(NSUInteger)index withSection:(id)section 256 | { 257 | if (!section) { 258 | NSAssert(section, @"Section should not be nil."); 259 | return; 260 | } 261 | 262 | NSUInteger sectionCount = [self countAllSections]; 263 | if (index >= sectionCount) { 264 | NSAssert(index < sectionCount, @"Index out of bounds!"); 265 | return; 266 | } 267 | 268 | INTUGroupedArraySectionContainer *sectionContainer = self.sectionContainers[index]; 269 | sectionContainer.section = section; 270 | _mutations++; 271 | } 272 | 273 | /** 274 | Replaces the object at the index path with another object. 275 | Performance: O(1) 276 | 277 | @param indexPath The index path of the object to replace. 278 | @param object The object with which to replace the existing object at the given index path. 279 | */ 280 | - (void)replaceObjectAtIndexPath:(NSIndexPath *)indexPath withObject:(id)object 281 | { 282 | if (!object || !indexPath) { 283 | NSAssert(object, @"Object should not be nil."); 284 | NSAssert(indexPath, @"Index path should not be nil."); 285 | return; 286 | } 287 | 288 | NSUInteger sectionIndex = [indexPath indexAtPosition:0]; 289 | NSUInteger objectIndex = [indexPath indexAtPosition:1]; 290 | 291 | if (sectionIndex >= [self countAllSections]) { 292 | NSAssert(sectionIndex < [self countAllSections], @"Section index out of bounds!"); 293 | return; 294 | } 295 | 296 | INTUMutableGroupedArraySectionContainer *sectionContainer = self.sectionContainers[sectionIndex]; 297 | NSMutableArray *objectsArray = sectionContainer.mutableObjects; 298 | 299 | if (objectIndex >= [objectsArray count]) { 300 | NSAssert(objectIndex < [objectsArray count], @"Object index out of bounds!"); 301 | return; 302 | } 303 | 304 | [objectsArray replaceObjectAtIndex:objectIndex withObject:object]; 305 | _mutations++; 306 | } 307 | 308 | #pragma mark Moving 309 | 310 | /** 311 | Moves the section at the index to a new index. 312 | Performance: O(n), where n is the number of sections 313 | 314 | @param fromIndex The index of the section to move. 315 | @param toIndex The index to move the section to. 316 | */ 317 | - (void)moveSectionAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex 318 | { 319 | NSUInteger sectionCount = [self countAllSections]; 320 | if (fromIndex >= sectionCount || toIndex >= sectionCount) { 321 | NSAssert(fromIndex < sectionCount, @"From index out of bounds!"); 322 | NSAssert(toIndex < sectionCount, @"To index out of bounds!"); 323 | return; 324 | } 325 | 326 | INTUGroupedArraySectionContainer *sectionContainer = self.sectionContainers[fromIndex]; 327 | [self.mutableSectionContainers removeObjectAtIndex:fromIndex]; 328 | [self.mutableSectionContainers insertObject:sectionContainer atIndex:toIndex]; 329 | _mutations++; 330 | } 331 | 332 | /** 333 | Moves the object at the index path to a new index path. The new index path must correspond to an existing section. 334 | Performance: O(n+m), where n is the number of sections, and m is the number of objects in the two sections 335 | 336 | @param fromIndexPath The index path of the object to move. 337 | @param toIndexPath The index path to move the object to. 338 | */ 339 | - (void)moveObjectAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath 340 | { 341 | if (!fromIndexPath || !toIndexPath) { 342 | NSAssert(fromIndexPath, @"From index path should not be nil!"); 343 | NSAssert(toIndexPath, @"To index path should not be nil!"); 344 | return; 345 | } 346 | 347 | NSUInteger fromSectionIndex = [fromIndexPath indexAtPosition:0]; 348 | NSUInteger fromObjectIndex = [fromIndexPath indexAtPosition:1]; 349 | NSUInteger toSectionIndex = [toIndexPath indexAtPosition:0]; 350 | NSUInteger toObjectIndex = [toIndexPath indexAtPosition:1]; 351 | 352 | NSUInteger sectionCount = [self countAllSections]; 353 | if (fromSectionIndex >= sectionCount || toSectionIndex >= sectionCount) { 354 | NSAssert(fromSectionIndex < sectionCount, @"From index path section out of bounds!"); 355 | NSAssert(toSectionIndex < sectionCount, @"To index path section out of bounds!"); 356 | return; 357 | } 358 | 359 | NSUInteger fromObjectCount = [self countObjectsInSectionAtIndex:fromSectionIndex]; 360 | NSUInteger toObjectCount; 361 | if (fromSectionIndex == toSectionIndex) { 362 | toObjectCount = fromObjectCount; 363 | } else { 364 | toObjectCount = [self countObjectsInSectionAtIndex:toSectionIndex] + 1; // add 1 to the current count since moving the object to the section will increase this 365 | } 366 | if (fromObjectIndex >= fromObjectCount || toObjectIndex >= toObjectCount) { 367 | NSAssert(fromObjectIndex < fromObjectCount, @"From index path row out of bounds!"); 368 | NSAssert(toObjectIndex < toObjectCount, @"To index path row out of bounds!"); 369 | return; 370 | } 371 | 372 | id object = [self objectAtIndexPath:fromIndexPath]; 373 | // Don't use [self removeObjectAtIndexPath:] here because we need to finish the insert before checking if the from section is empty 374 | INTUMutableGroupedArraySectionContainer *sectionContainer = self.sectionContainers[fromSectionIndex]; 375 | NSMutableArray *objectsArray = sectionContainer.mutableObjects; 376 | [objectsArray removeObjectAtIndex:fromObjectIndex]; 377 | [self insertObject:object atIndexPath:toIndexPath]; 378 | // Check if moving this object left its section empty; if so, remove it 379 | if ([objectsArray count] == 0) { 380 | [self removeSectionAtIndex:fromSectionIndex]; 381 | } 382 | _mutations++; 383 | } 384 | 385 | #pragma mark Exchanging 386 | 387 | /** 388 | Exchanges the section at one index with a section at another index. 389 | Performance: O(1) 390 | 391 | @param index1 The index of the section to replace with the section at index2. 392 | @param index2 The index of the section to replace with the section at index1. 393 | */ 394 | - (void)exchangeSectionAtIndex:(NSUInteger)index1 withSectionAtIndex:(NSUInteger)index2 395 | { 396 | NSUInteger sectionCount = [self countAllSections]; 397 | if (index1 >= sectionCount || index2 >= sectionCount) { 398 | NSAssert(index1 < sectionCount, @"Index 1 out of bounds!"); 399 | NSAssert(index2 < sectionCount, @"Index 2 out of bounds!"); 400 | return; 401 | } 402 | [self.mutableSectionContainers exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; 403 | _mutations++; 404 | } 405 | 406 | /** 407 | Exchanges the object at one index path with an object at another index path. 408 | Performance: O(1) 409 | 410 | @param indexPath1 The index path of the object to replace with the object at indexPath2. 411 | @param indexPath2 The index path of the object to replace with the object at indexPath1. 412 | */ 413 | - (void)exchangeObjectAtIndexPath:(NSIndexPath *)indexPath1 withObjectAtIndexPath:(NSIndexPath *)indexPath2 414 | { 415 | if (!indexPath1 || !indexPath2) { 416 | NSAssert(indexPath1, @"Index path 1 should not be nil!"); 417 | NSAssert(indexPath2, @"Index path 2 should not be nil!"); 418 | return; 419 | } 420 | 421 | NSUInteger sectionIndex1 = [indexPath1 indexAtPosition:0]; 422 | NSUInteger objectIndex1 = [indexPath1 indexAtPosition:1]; 423 | NSUInteger sectionIndex2 = [indexPath2 indexAtPosition:0]; 424 | NSUInteger objectIndex2 = [indexPath2 indexAtPosition:1]; 425 | 426 | NSUInteger sectionCount = [self countAllSections]; 427 | if (sectionIndex1 >= sectionCount || sectionIndex2 >= sectionCount) { 428 | NSAssert(sectionIndex1 < sectionCount, @"Index path 1 section out of bounds!"); 429 | NSAssert(sectionIndex2 < sectionCount, @"Index path 2 section out of bounds!"); 430 | return; 431 | } 432 | 433 | NSUInteger objectCount1 = [self countObjectsInSectionAtIndex:sectionIndex1]; 434 | NSUInteger objectCount2 = [self countObjectsInSectionAtIndex:sectionIndex2]; 435 | if (objectIndex1 >= objectCount1 || objectIndex2 >= objectCount2) { 436 | NSAssert(objectIndex1 < objectCount1, @"Index path 1 row out of bounds!"); 437 | NSAssert(objectIndex2 < objectCount2, @"Index path 2 row out of bounds!"); 438 | return; 439 | } 440 | 441 | INTUMutableGroupedArraySectionContainer *sectionContainer1 = self.sectionContainers[sectionIndex1]; 442 | INTUMutableGroupedArraySectionContainer *sectionContainer2 = self.sectionContainers[sectionIndex2]; 443 | id object1 = sectionContainer1.objects[objectIndex1]; 444 | sectionContainer1.mutableObjects[objectIndex1] = sectionContainer2.objects[objectIndex2]; 445 | sectionContainer2.mutableObjects[objectIndex2] = object1; 446 | _mutations++; 447 | } 448 | 449 | #pragma mark Removing 450 | 451 | /** 452 | Removes all objects and sections. 453 | Performance: O(n), where n is the total number of objects across all sections 454 | */ 455 | - (void)removeAllObjects 456 | { 457 | [self.mutableSectionContainers removeAllObjects]; 458 | _mutations++; 459 | } 460 | 461 | /** 462 | Removes the section and all objects in it. 463 | Performance: O(n), where n is the number of sections 464 | 465 | @param section The section to remove. 466 | */ 467 | - (void)removeSection:(id)section 468 | { 469 | if (!section) { 470 | NSAssert(section, @"Section should not be nil."); 471 | return; 472 | } 473 | NSUInteger indexToRemove = [self indexOfSection:section]; 474 | if (indexToRemove != NSNotFound) { 475 | [self removeSectionAtIndex:indexToRemove]; 476 | } 477 | } 478 | 479 | /** 480 | Removes the section at the index and all objects in it. 481 | An exception will be raised if the index is out of bounds. 482 | Performance: O(n), where n is the number of sections 483 | 484 | @param index The index of the section to remove. 485 | */ 486 | - (void)removeSectionAtIndex:(NSUInteger)index 487 | { 488 | if (index >= [self countAllSections]) { 489 | NSAssert(index < [self countAllSections], @"Index out of bounds!"); 490 | return; 491 | } 492 | [self.mutableSectionContainers removeObjectAtIndex:index]; 493 | _mutations++; 494 | } 495 | 496 | /** 497 | Removes all occurrences of the object across all sections. Empty sections will be removed. 498 | Performance: O(n), where n is the total number of objects across all sections 499 | 500 | @param object The object to remove. 501 | */ 502 | - (void)removeObject:(id)object 503 | { 504 | if (!object) { 505 | NSAssert(object, @"Object should not be nil."); 506 | return; 507 | } 508 | NSMutableArray *arraySectionsToRemove = [NSMutableArray new]; 509 | for (INTUMutableGroupedArraySectionContainer *sectionContainer in self.sectionContainers) { 510 | NSMutableArray *objectsArray = sectionContainer.mutableObjects; 511 | [objectsArray removeObject:object]; 512 | if ([objectsArray count] == 0) { 513 | [arraySectionsToRemove addObject:sectionContainer]; 514 | } 515 | } 516 | for (INTUGroupedArraySectionContainer *sectionContainer in arraySectionsToRemove) { 517 | [self.mutableSectionContainers removeObject:sectionContainer]; 518 | } 519 | _mutations++; 520 | } 521 | 522 | /** 523 | Removes all occurrences of the object from the section. Empty sections will be removed. 524 | Performance: O(n+m), where n is the number of sections, and m is the number of objects in the section 525 | 526 | @param object The object to remove. 527 | @param section The section to remove the object from. 528 | */ 529 | - (void)removeObject:(id)object fromSection:(id)section 530 | { 531 | if (!object || !section) { 532 | NSAssert(object, @"Object should not be nil."); 533 | NSAssert(section, @"Section should not be nil."); 534 | return; 535 | } 536 | NSMutableArray *objectsArray = [self _objectsArrayForSection:section withSectionIndexHint:NSNotFound]; 537 | if (objectsArray == nil) { 538 | // Section does not exist 539 | return; 540 | } 541 | [objectsArray removeObject:object]; 542 | if ([objectsArray count] == 0) { 543 | [self removeSection:section]; 544 | } 545 | _mutations++; 546 | } 547 | 548 | /** 549 | Removes the object at the index from the section. Empty sections will be removed. 550 | An exception will be raised if the index is out of bounds. 551 | Performance: O(n+m), where n is the number of sections, and m is the number of objects in the section 552 | 553 | @param index The index of the object to remove. 554 | @param section The section to remove the object from. 555 | */ 556 | - (void)removeObjectAtIndex:(NSUInteger)index fromSection:(id)section 557 | { 558 | if (!section) { 559 | NSAssert(section, @"Section should not be nil."); 560 | return; 561 | } 562 | NSMutableArray *objectsArray = [self _objectsArrayForSection:section withSectionIndexHint:NSNotFound]; 563 | if (objectsArray == nil) { 564 | // Section does not exist 565 | return; 566 | } 567 | if (index >= [objectsArray count]) { 568 | NSAssert(index < [objectsArray count], @"Index out of bounds!"); 569 | return; 570 | } 571 | [objectsArray removeObjectAtIndex:index]; 572 | if ([objectsArray count] == 0) { 573 | [self removeSection:section]; 574 | } 575 | _mutations++; 576 | } 577 | 578 | /** 579 | Removes the object at the index path. Empty sections will be removed. 580 | An exception will be raised if the index is out of bounds. 581 | Performance: O(n), where n is the number of objects in the section 582 | 583 | @param indexPath The index path of the object to remove. 584 | */ 585 | - (void)removeObjectAtIndexPath:(NSIndexPath *)indexPath 586 | { 587 | if (!indexPath) { 588 | NSAssert(indexPath, @"Index path should not be nil."); 589 | return; 590 | } 591 | NSUInteger sectionIndex = [indexPath indexAtPosition:0]; 592 | NSUInteger objectIndex = [indexPath indexAtPosition:1]; 593 | if (sectionIndex >= [self countAllSections]) { 594 | NSAssert(sectionIndex < [self countAllSections], @"Section index out of bounds!"); 595 | return; 596 | } 597 | INTUMutableGroupedArraySectionContainer *sectionContainer = self.sectionContainers[sectionIndex]; 598 | NSMutableArray *objectsArray = sectionContainer.mutableObjects; 599 | if (objectIndex >= [objectsArray count]) { 600 | NSAssert(objectIndex < [objectsArray count], @"Row index out of bounds!"); 601 | return; 602 | } 603 | [objectsArray removeObjectAtIndex:objectIndex]; 604 | if ([objectsArray count] == 0) { 605 | [self removeSectionAtIndex:sectionIndex]; 606 | } 607 | _mutations++; 608 | } 609 | 610 | #pragma mark Filtering 611 | 612 | /** 613 | Evaluates the section & object predicates against all sections & objects and removes those that do not match. Empty sections will be removed. 614 | 615 | @param sectionPredicate The predicate to evaluate against the sections. 616 | @param objectPredicate The predicate to evaluate against the objects. 617 | */ 618 | - (void)filterUsingSectionPredicate:(NSPredicate *)sectionPredicate objectPredicate:(NSPredicate *)objectPredicate 619 | { 620 | if (sectionPredicate || objectPredicate) { 621 | NSMutableArray *sectionContainersToRemove = [NSMutableArray new]; 622 | for (INTUMutableGroupedArraySectionContainer *sectionContainer in self.sectionContainers) { 623 | if (sectionPredicate && [sectionPredicate evaluateWithObject:sectionContainer.section] == NO) { 624 | [sectionContainersToRemove addObject:sectionContainer]; 625 | } else if (objectPredicate) { 626 | NSMutableArray *objectsArray = sectionContainer.mutableObjects; 627 | [objectsArray filterUsingPredicate:objectPredicate]; 628 | if ([objectsArray count] == 0) { 629 | [sectionContainersToRemove addObject:sectionContainer]; 630 | } 631 | } 632 | } 633 | for (INTUGroupedArraySectionContainer *sectionContainer in sectionContainersToRemove) { 634 | [self.mutableSectionContainers removeObject:sectionContainer]; 635 | } 636 | } 637 | _mutations++; 638 | } 639 | 640 | #pragma mark Sorting 641 | 642 | /** 643 | Sorts the sections using the section comparator, and the objects in each section using the object comparator. 644 | 645 | @param sectionCmptr A comparator block used to sort sections, or nil if no section sorting is desired. 646 | @param objectCmptr A comparator block used to sort objects in each section, or nil if no object sorting is desired. 647 | */ 648 | - (void)sortUsingSectionComparator:(NSComparator)sectionCmptr objectComparator:(NSComparator)objectCmptr 649 | { 650 | if (sectionCmptr) { 651 | [self.mutableSectionContainers sortUsingComparator:^NSComparisonResult(INTUGroupedArraySectionContainer *arraySection1, INTUGroupedArraySectionContainer *arraySection2) { 652 | return sectionCmptr(arraySection1.section, arraySection2.section); 653 | }]; 654 | } 655 | if (objectCmptr) { 656 | for (INTUMutableGroupedArraySectionContainer *sectionContainer in self.sectionContainers) { 657 | [sectionContainer.mutableObjects sortUsingComparator:objectCmptr]; 658 | } 659 | } 660 | _mutations++; 661 | } 662 | 663 | #pragma mark Internal Helper Methods 664 | 665 | /** 666 | Returns the objects in the section, without copying the array. Passing an accurate hint for the section index 667 | will dramatically accelerate performance when there are a large number of sections, as it will avoid having to 668 | call -[self indexOfSection:] to find the section. 669 | Performance: O(1) assuming an accurate section index hint; otherwise O(n), where n is the number of sections 670 | 671 | @param section The section to get the objects of. 672 | @param sectionIndexHint An optional hint to the index of the section. (Pass NSNotFound to ignore the hint.) 673 | @return The mutable array of all the objects in the section, or nil if the section does not exist. 674 | */ 675 | - (NSMutableArray *)_objectsArrayForSection:(id)section withSectionIndexHint:(NSUInteger)sectionIndexHint 676 | { 677 | if (sectionIndexHint != NSNotFound && sectionIndexHint < [self countAllSections]) { 678 | // Use the hint first to see if it correctly locates the section 679 | id sectionAtHint = [self sectionAtIndex:sectionIndexHint]; 680 | if ([sectionAtHint isEqual:section]) { 681 | // The hint worked! 682 | INTUMutableGroupedArraySectionContainer *sectionContainer = self.sectionContainers[sectionIndexHint]; 683 | return sectionContainer.mutableObjects; 684 | } 685 | } 686 | 687 | // Don't have a hint to use, or the hint was out of bounds, or the hint was wrong 688 | NSUInteger sectionIndex = [self indexOfSection:section]; 689 | if (sectionIndex == NSNotFound) { 690 | return nil; 691 | } else { 692 | INTUMutableGroupedArraySectionContainer *sectionContainer = self.sectionContainers[sectionIndex]; 693 | return sectionContainer.mutableObjects; 694 | } 695 | } 696 | 697 | @end 698 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/Internal/INTUGroupedArrayDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUGroupedArrayDefines.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #ifndef INTUGroupedArrayDefines_h 28 | #define INTUGroupedArrayDefines_h 29 | 30 | #if __has_feature(nullability) 31 | # define GA__INTU_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN 32 | # define GA__INTU_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END 33 | # define GA__INTU_NULLABLE nullable 34 | #else 35 | # define GA__INTU_ASSUME_NONNULL_BEGIN 36 | # define GA__INTU_ASSUME_NONNULL_END 37 | # define GA__INTU_NULLABLE 38 | #endif 39 | 40 | #if __has_feature(objc_generics) 41 | # define GA__INTU_GENERICS(type, ...) type<__VA_ARGS__> 42 | # define GA__INTU_GENERICS_TYPE(type) type 43 | #else 44 | # define GA__INTU_GENERICS(type, ...) type 45 | # define GA__INTU_GENERICS_TYPE(type) id 46 | #endif 47 | 48 | #endif /* INTUGroupedArrayDefines_h */ 49 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/Internal/INTUGroupedArrayInternal.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUGroupedArrayInternal.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #ifndef INTUGroupedArrayInternal_h 28 | #define INTUGroupedArrayInternal_h 29 | 30 | #import "INTUGroupedArray.h" 31 | #import "INTUIndexPair.h" 32 | #import "INTUGroupedArraySectionContainer.h" 33 | 34 | GA__INTU_ASSUME_NONNULL_BEGIN 35 | 36 | /** 37 | A category on INTUGroupedArray that exposes some private internal properties and methods for 38 | subclasses to access. 39 | */ 40 | @interface GA__INTU_GENERICS(INTUGroupedArray, SectionType, ObjectType) (Internal) 41 | 42 | // An array of INTUGroupedArraySectionContainer objects. 43 | @property (nonatomic, strong) GA__INTU_GENERICS(NSArray, GA__INTU_GENERICS(INTUGroupedArraySectionContainer, SectionType, ObjectType) *) *sectionContainers; 44 | 45 | /** 46 | Returns the memory address of the _mutations instance variable. 47 | */ 48 | - (unsigned long *)_mutationsPtr; 49 | 50 | /** 51 | An internally exposed variant of objectAtIndexPath that takes an INTUIndexPair instead of NSIndexPath. 52 | This method may be called directly instead of the NSIndexPath variant in order to avoid the overhead of 53 | instantiating many NSIndexPath objects (e.g. during fast enumeration). 54 | 55 | An exception will be raised if the index pair is out of bounds. 56 | 57 | @param indexPair The index pair of the object. 58 | @return The object at the index pair, or nil if the index pair is out of bounds. 59 | */ 60 | - (GA__INTU_GENERICS_TYPE(ObjectType))_objectAtIndexPair:(INTUIndexPair)indexPair; 61 | 62 | @end 63 | 64 | GA__INTU_ASSUME_NONNULL_END 65 | 66 | #endif /* INTUGroupedArrayInternal_h */ 67 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/Internal/INTUGroupedArraySectionContainer.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUGroupedArraySectionContainer.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import "INTUGroupedArrayDefines.h" 29 | 30 | GA__INTU_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | A helper object used to encapsulate the section and its associated array of objects. 34 | */ 35 | @interface GA__INTU_GENERICS(INTUGroupedArraySectionContainer, SectionType, ObjectType) : NSObject 36 | 37 | @property (nonatomic, strong) GA__INTU_GENERICS_TYPE(SectionType) section; 38 | @property (nonatomic, strong) GA__INTU_GENERICS(NSArray, ObjectType) *objects; 39 | 40 | /** Returns a new section container with the given section. */ 41 | + (instancetype)sectionContainerWithSection:(GA__INTU_GENERICS_TYPE(SectionType))section; 42 | 43 | /** Returns a new section container that is a deep copy of the section container, copying the section and objects. */ 44 | - (instancetype)initWithSectionContainer:(GA__INTU_GENERICS(INTUGroupedArraySectionContainer, SectionType, ObjectType) *)sectionContainer copyItems:(BOOL)copyItems; 45 | 46 | @end 47 | 48 | 49 | /** 50 | A helper object used to encapsulate the section and its associated mutable array of objects. 51 | */ 52 | @interface GA__INTU_GENERICS(INTUMutableGroupedArraySectionContainer, SectionType, ObjectType) : INTUGroupedArraySectionContainer 53 | 54 | /** Exposes the superclass objects instance variable typecast to NSMutableArray. */ 55 | @property (nonatomic, strong) GA__INTU_GENERICS(NSMutableArray, ObjectType) *mutableObjects; 56 | 57 | GA__INTU_ASSUME_NONNULL_END 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/Internal/INTUGroupedArraySectionContainer.m: -------------------------------------------------------------------------------- 1 | // 2 | // INTUGroupedArraySectionContainer.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import "INTUGroupedArraySectionContainer.h" 28 | 29 | @implementation INTUGroupedArraySectionContainer 30 | 31 | /** 32 | Returns a new section container with the given section. 33 | */ 34 | + (instancetype)sectionContainerWithSection:(id)section 35 | { 36 | INTUGroupedArraySectionContainer *sectionContainer = [self new]; 37 | sectionContainer.section = section; 38 | sectionContainer.objects = [NSArray new]; 39 | return sectionContainer; 40 | } 41 | 42 | /** 43 | Perform a shallow copy of the section container, so that the section and objects are not copied. 44 | */ 45 | - (instancetype)copyWithZone:(NSZone *)zone 46 | { 47 | __typeof(self) copy = [[[self class] allocWithZone:zone] init]; 48 | copy.section = self.section; 49 | copy.objects = [[NSArray allocWithZone:zone] initWithArray:self.objects copyItems:NO]; 50 | return copy; 51 | } 52 | 53 | /** 54 | Perform a shallow copy of the section container, so that the section and objects are not copied. 55 | */ 56 | - (instancetype)mutableCopyWithZone:(NSZone *)zone 57 | { 58 | __typeof(self) copy = [[[self class] allocWithZone:zone] init]; 59 | copy.section = self.section; 60 | copy.objects = [[NSMutableArray allocWithZone:zone] initWithArray:self.objects copyItems:NO]; 61 | return copy; 62 | } 63 | 64 | /** 65 | Creates and returns a new section container with the contents of a given section container, optionally copying the section & objects. 66 | */ 67 | - (instancetype)initWithSectionContainer:(INTUGroupedArraySectionContainer *)sectionContainer copyItems:(BOOL)copyItems 68 | { 69 | __typeof(self) newSectionContainer = [[[self class] alloc] init]; 70 | newSectionContainer.section = copyItems ? [sectionContainer.section copy] : sectionContainer.section; 71 | newSectionContainer.objects = [[NSArray alloc] initWithArray:sectionContainer.objects copyItems:copyItems]; 72 | return newSectionContainer; 73 | } 74 | 75 | - (id)initWithCoder:(NSCoder *)aDecoder 76 | { 77 | self = [self init]; 78 | if (self) { 79 | self.section = [aDecoder decodeObjectForKey:@"section"]; 80 | self.objects = [aDecoder decodeObjectForKey:@"objects"]; 81 | } 82 | return self; 83 | } 84 | 85 | - (void)encodeWithCoder:(NSCoder *)aCoder 86 | { 87 | if (_section) { 88 | [aCoder encodeObject:_section forKey:@"section"]; 89 | } 90 | if (_objects) { 91 | [aCoder encodeObject:_objects forKey:@"objects"]; 92 | } 93 | } 94 | 95 | @end 96 | 97 | @implementation INTUMutableGroupedArraySectionContainer 98 | 99 | /** 100 | Returns a new section container with the given section. 101 | */ 102 | + (instancetype)sectionContainerWithSection:(id)section 103 | { 104 | INTUMutableGroupedArraySectionContainer *sectionContainer = [self new]; 105 | sectionContainer.section = section; 106 | sectionContainer.objects = [NSMutableArray new]; 107 | return sectionContainer; 108 | } 109 | 110 | /** 111 | Creates and returns a new mutable section container with the contents of a given section container, optionally copying the section & objects. 112 | */ 113 | - (instancetype)initWithSectionContainer:(INTUGroupedArraySectionContainer *)sectionContainer copyItems:(BOOL)copyItems 114 | { 115 | __typeof(self) newSectionContainer = [[[self class] alloc] init]; 116 | newSectionContainer.section = copyItems ? [sectionContainer.section copy] : sectionContainer.section; 117 | newSectionContainer.objects = [[NSMutableArray alloc] initWithArray:sectionContainer.objects copyItems:copyItems]; 118 | return newSectionContainer; 119 | } 120 | 121 | - (NSMutableArray *)mutableObjects 122 | { 123 | return (NSMutableArray *)[super objects]; 124 | } 125 | 126 | - (void)setMutableObjects:(NSMutableArray *)mutableObjects 127 | { 128 | [super setObjects:mutableObjects]; 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/Internal/INTUIndexPair.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUIndexPair.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #ifndef INTUIndexPair_h 28 | #define INTUIndexPair_h 29 | 30 | #import 31 | #import "INTUGroupedArrayDefines.h" 32 | 33 | GA__INTU_ASSUME_NONNULL_BEGIN 34 | 35 | 36 | struct INTUIndexPair { 37 | NSUInteger sectionIndex; 38 | NSUInteger objectIndex; 39 | }; 40 | /** 41 | A struct that contains a section index and object index. 42 | Offers better performance than NSIndexPath, which has the overhead of being an Objective-C object. 43 | */ 44 | typedef struct INTUIndexPair INTUIndexPair; 45 | 46 | /** 47 | Returns an INTUIndexPair with the section index and object index. 48 | */ 49 | static inline INTUIndexPair INTUIndexPairMake(NSUInteger sectionIndex, NSUInteger objectIndex) 50 | { 51 | INTUIndexPair indexPair; indexPair.sectionIndex = sectionIndex; indexPair.objectIndex = objectIndex; return indexPair; 52 | } 53 | 54 | /** 55 | Returns an INTUIndexPair based on the first two indices in the NSIndexPath object. 56 | */ 57 | static inline INTUIndexPair INTUIndexPairConvert(NSIndexPath *indexPath) 58 | { 59 | INTUIndexPair indexPair; indexPair.sectionIndex = [indexPath indexAtPosition:0]; indexPair.objectIndex = [indexPath indexAtPosition:1]; return indexPair; 60 | } 61 | 62 | GA__INTU_ASSUME_NONNULL_END 63 | 64 | #endif /* INTUIndexPair_h */ 65 | -------------------------------------------------------------------------------- /Source/INTUGroupedArray/Internal/INTUMutableGroupedArrayInternal.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTUMutableGroupedArrayInternal.h 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #ifndef INTUMutableGroupedArrayInternal_h 28 | #define INTUMutableGroupedArrayInternal_h 29 | 30 | #import "INTUMutableGroupedArray.h" 31 | 32 | GA__INTU_ASSUME_NONNULL_BEGIN 33 | 34 | /** 35 | A category on INTUMutableGroupedArray that exposes some private internal properties and methods for 36 | subclasses to access. 37 | */ 38 | @interface GA__INTU_GENERICS(INTUMutableGroupedArray, SectionType, ObjectType) (Internal) 39 | 40 | // A mutable array of INTUMutableGroupedArraySectionContainer objects. 41 | @property (nonatomic) GA__INTU_GENERICS(NSMutableArray, GA__INTU_GENERICS(INTUMutableGroupedArraySectionContainer, SectionType, ObjectType) *) *mutableSectionContainers; 42 | 43 | - (GA__INTU_GENERICS(NSMutableArray, ObjectType) *)_objectsArrayForSection:(GA__INTU_GENERICS_TYPE(SectionType))section withSectionIndexHint:(NSUInteger)sectionIndexHint; 44 | 45 | @end 46 | 47 | GA__INTU_ASSUME_NONNULL_END 48 | 49 | #endif /* INTUMutableGroupedArrayInternal_h */ 50 | -------------------------------------------------------------------------------- /Source/Swift/GroupedArray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GroupedArray.swift 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | import Foundation 28 | 29 | 30 | public func ==(lhs: GroupedArray, rhs: GroupedArray) -> Bool 31 | { 32 | return lhs.intuGroupedArray.isEqualToGroupedArray(rhs.intuGroupedArray) 33 | } 34 | 35 | 36 | // MARK: GroupedArray 37 | 38 | public class GroupedArray: SequenceType, Equatable, CustomStringConvertible, CustomDebugStringConvertible, ArrayLiteralConvertible 39 | { 40 | private var intuGroupedArray: INTUGroupedArray 41 | 42 | public init() 43 | { 44 | intuGroupedArray = INTUGroupedArray() 45 | } 46 | 47 | public init(groupedArray: GroupedArray) 48 | { 49 | intuGroupedArray = INTUGroupedArray(groupedArray: groupedArray.intuGroupedArray) 50 | } 51 | 52 | public init(groupedArray: GroupedArray, copyItems: Bool) 53 | { 54 | intuGroupedArray = INTUGroupedArray(groupedArray: groupedArray.intuGroupedArray, copyItems: copyItems) 55 | } 56 | 57 | public init(array: [O]) 58 | { 59 | intuGroupedArray = INTUGroupedArray(array: array) 60 | } 61 | 62 | public required init(arrayLiteral elements: AnyObject...) 63 | { 64 | intuGroupedArray = INTUGroupedArray.literal(elements) 65 | } 66 | 67 | 68 | public func copy() -> GroupedArray 69 | { 70 | let newGroupedArray = GroupedArray() 71 | newGroupedArray.intuGroupedArray = intuGroupedArray.copy() as! INTUGroupedArray 72 | return newGroupedArray 73 | } 74 | 75 | public func mutableCopy() -> MutableGroupedArray 76 | { 77 | let newGroupedArray = MutableGroupedArray() 78 | newGroupedArray.intuGroupedArray = intuGroupedArray.mutableCopy() as! INTUMutableGroupedArray 79 | return newGroupedArray 80 | } 81 | 82 | 83 | public var description: String { 84 | return intuGroupedArray.description 85 | } 86 | 87 | public var debugDescription: String { 88 | return intuGroupedArray.description 89 | } 90 | 91 | 92 | subscript (sectionIndex: Int) -> S! { 93 | return sectionAtIndex(sectionIndex) 94 | } 95 | 96 | subscript (sectionIndex: Int, objectIndex: Int) -> O! { 97 | return objectAtIndexPath(INTUGroupedArray.indexPathForRow(UInt(objectIndex), inSection: UInt(sectionIndex))) 98 | } 99 | 100 | subscript (indexPath: NSIndexPath) -> O! { 101 | return objectAtIndexPath(indexPath) 102 | } 103 | 104 | 105 | public func sectionAtIndex(index: Int) -> S! 106 | { 107 | return intuGroupedArray.sectionAtIndex(UInt(index)) as? S 108 | } 109 | 110 | public func countAllSections() -> Int 111 | { 112 | return Int(intuGroupedArray.countAllSections()) 113 | } 114 | 115 | public func allSections() -> [S] 116 | { 117 | return intuGroupedArray.allSections() as! [S] 118 | } 119 | 120 | public func containsSection(section: S) -> Bool 121 | { 122 | return intuGroupedArray.containsSection(section) 123 | } 124 | 125 | public func indexOfSection(section: S) -> Int? 126 | { 127 | let index = Int(intuGroupedArray.indexOfSection(section)) 128 | return (index == NSNotFound) ? nil : index 129 | } 130 | 131 | 132 | public func objectAtIndex(index: Int, inSection section: S) -> O! 133 | { 134 | return intuGroupedArray.objectAtIndex(UInt(index), inSection: section) as? O 135 | } 136 | 137 | public func objectAtIndexPath(indexPath: NSIndexPath) -> O! 138 | { 139 | return intuGroupedArray.objectAtIndexPath(indexPath) as? O 140 | } 141 | 142 | public func firstObject() -> O? 143 | { 144 | return intuGroupedArray.firstObject() as? O 145 | } 146 | 147 | public func lastObject() -> O? 148 | { 149 | return intuGroupedArray.lastObject() as? O 150 | } 151 | 152 | public func containsObject(object: O) -> Bool 153 | { 154 | return intuGroupedArray.containsObject(object) 155 | } 156 | 157 | public func indexPathOfObject(object: O) -> NSIndexPath? 158 | { 159 | return intuGroupedArray.indexPathOfObject(object) 160 | } 161 | 162 | public func containsObject(object: O, inSection section: S) -> Bool 163 | { 164 | return intuGroupedArray.containsObject(object, inSection: section) 165 | } 166 | 167 | public func indexOfObject(object: O, inSection section: S) -> Int? 168 | { 169 | let index = Int(intuGroupedArray.indexOfObject(object, inSection: section)) 170 | return (index == NSNotFound) ? nil : index 171 | } 172 | 173 | public func countObjectsInSection(section: S) -> Int 174 | { 175 | return Int(intuGroupedArray.countObjectsInSection(section)) 176 | } 177 | 178 | public func countObjectsInSectionAtIndex(sectionIndex: Int) -> Int 179 | { 180 | return Int(intuGroupedArray.countObjectsInSectionAtIndex(UInt(sectionIndex))) 181 | } 182 | 183 | public func objectsInSection(section: S) -> [O] 184 | { 185 | return intuGroupedArray.objectsInSection(section) as! [O] 186 | } 187 | 188 | public func objectsInSectionAtIndex(sectionIndex: Int) -> [O] 189 | { 190 | return intuGroupedArray.objectsInSectionAtIndex(UInt(sectionIndex)) as! [O] 191 | } 192 | 193 | public func countAllObjects() -> Int 194 | { 195 | return Int(intuGroupedArray.countAllObjects()) 196 | } 197 | 198 | public func allObjects() -> [O] 199 | { 200 | return intuGroupedArray.allObjects() as! [O] 201 | } 202 | 203 | 204 | public func enumerateSections(block: (section: S, index: Int, stop: UnsafeMutablePointer) -> Void) 205 | { 206 | intuGroupedArray.enumerateSectionsUsingBlock { (section: AnyObject!, index: UInt, stop: UnsafeMutablePointer) -> Void in 207 | block(section: section as! S, index: Int(index), stop: stop) 208 | } 209 | } 210 | 211 | public func enumerateSections(options: NSEnumerationOptions, block: (section: S, index: Int, stop: UnsafeMutablePointer) -> Void) 212 | { 213 | intuGroupedArray.enumerateSectionsWithOptions(options) { (section: AnyObject!, index: UInt, stop: UnsafeMutablePointer) -> Void in 214 | block(section: section as! S, index: Int(index), stop: stop) 215 | } 216 | } 217 | 218 | public func enumerateObjects(block: (object: O, indexPath: NSIndexPath, stop: UnsafeMutablePointer) -> Void) 219 | { 220 | intuGroupedArray.enumerateObjectsUsingBlock { (object: AnyObject!, indexPath: NSIndexPath!, stop: UnsafeMutablePointer) -> Void in 221 | block(object: object as! O, indexPath: indexPath, stop: stop) 222 | } 223 | } 224 | 225 | public func enumerateObjects(options: NSEnumerationOptions, block: (object: O, indexPath: NSIndexPath, stop: UnsafeMutablePointer) -> Void) 226 | { 227 | intuGroupedArray.enumerateObjectsWithOptions(options) { (object: AnyObject!, indexPath: NSIndexPath!, stop: UnsafeMutablePointer) -> Void in 228 | block(object: object as! O, indexPath: indexPath, stop: stop) 229 | } 230 | } 231 | 232 | public func enumerateObjectsInSectionAtIndex(sectionIndex: Int, block: (object: O, indexPath: NSIndexPath, stop: UnsafeMutablePointer) -> Void) 233 | { 234 | intuGroupedArray.enumerateObjectsInSectionAtIndex(UInt(sectionIndex)) { (object: AnyObject!, indexPath: NSIndexPath!, stop: UnsafeMutablePointer) -> Void in 235 | block(object: object as! O, indexPath: indexPath, stop: stop) 236 | } 237 | } 238 | 239 | public func enumerateObjectsInSectionAtIndex(sectionIndex: Int, withOptions options: NSEnumerationOptions, block: (object: O, indexPath: NSIndexPath, stop: UnsafeMutablePointer) -> Void) 240 | { 241 | intuGroupedArray.enumerateObjectsInSectionAtIndex(UInt(sectionIndex), withOptions: options) { (object: AnyObject!, indexPath: NSIndexPath!, stop: UnsafeMutablePointer) -> Void in 242 | block(object: object as! O, indexPath: indexPath, stop: stop) 243 | } 244 | } 245 | 246 | 247 | public func generate() -> AnyGenerator<(section: S, object: O)> 248 | { 249 | let groupedArray = self 250 | var sectionIndex = 0 251 | var objectIndex = 0 252 | return AnyGenerator { 253 | let sectionCount = groupedArray.countAllSections() 254 | if sectionIndex < sectionCount { 255 | let section = groupedArray.sectionAtIndex(sectionIndex) 256 | let objectCount = groupedArray.countObjectsInSectionAtIndex(sectionIndex) 257 | if objectIndex < objectCount { 258 | let object = groupedArray.objectAtIndexPath(INTUGroupedArray.indexPathForRow(UInt(objectIndex), inSection: UInt(sectionIndex))) 259 | objectIndex += 1 260 | return (section, object) 261 | } 262 | objectIndex = 0 263 | sectionIndex += 1 264 | } 265 | return nil 266 | } 267 | } 268 | 269 | public func sectionEnumerator() -> GroupedArraySectionEnumerator 270 | { 271 | return GroupedArraySectionEnumerator(intuGroupedArray.sectionEnumerator()) 272 | } 273 | 274 | public func reverseSectionEnumerator() -> GroupedArraySectionEnumerator 275 | { 276 | return GroupedArraySectionEnumerator(intuGroupedArray.reverseSectionEnumerator()) 277 | } 278 | 279 | public func objectEnumerator() -> GroupedArrayObjectEnumerator 280 | { 281 | return GroupedArrayObjectEnumerator(intuGroupedArray.objectEnumerator()) 282 | } 283 | 284 | public func reverseObjectEnumerator() -> GroupedArrayObjectEnumerator 285 | { 286 | return GroupedArrayObjectEnumerator(intuGroupedArray.reverseObjectEnumerator()) 287 | } 288 | 289 | 290 | public func indexOfSectionPassingTest(predicate: (section: S, index: Int, stop: UnsafeMutablePointer) -> Bool) -> Int? 291 | { 292 | let index = Int(intuGroupedArray.indexOfSectionPassingTest { (section: AnyObject!, index: UInt, stop: UnsafeMutablePointer) -> Bool in 293 | return predicate(section: section as! S, index: Int(index), stop: stop) 294 | }) 295 | return (index == NSNotFound) ? nil : index 296 | } 297 | 298 | public func indexPathOfObjectPassingTest(predicate: (object: O, indexPath: NSIndexPath, stop: UnsafeMutablePointer) -> Bool) -> NSIndexPath? 299 | { 300 | return intuGroupedArray.indexPathOfObjectPassingTest { (object: AnyObject!, indexPath: NSIndexPath!, stop: UnsafeMutablePointer) -> Bool in 301 | return predicate(object: object as! O, indexPath: indexPath, stop: stop) 302 | } 303 | } 304 | 305 | 306 | public func filtered(sectionPredicate sectionPredicate: NSPredicate, objectPredicate: NSPredicate) -> GroupedArray 307 | { 308 | let newGroupedArray: GroupedArray = GroupedArray() 309 | newGroupedArray.intuGroupedArray = intuGroupedArray.filteredGroupedArrayUsingSectionPredicate(sectionPredicate, objectPredicate: objectPredicate) 310 | return newGroupedArray 311 | } 312 | 313 | public func sorted(sectionComparator sectionComparator: NSComparator, objectComparator: NSComparator) -> GroupedArray 314 | { 315 | let newGroupedArray: GroupedArray = GroupedArray() 316 | newGroupedArray.intuGroupedArray = intuGroupedArray.sortedGroupedArrayUsingSectionComparator(sectionComparator, objectComparator: objectComparator) 317 | return newGroupedArray 318 | } 319 | } 320 | 321 | 322 | // MARK: MutableGroupedArray 323 | 324 | public class MutableGroupedArray: GroupedArray 325 | { 326 | private var intuMutableGroupedArray: INTUMutableGroupedArray 327 | { 328 | return intuGroupedArray as! INTUMutableGroupedArray 329 | } 330 | 331 | override public init() 332 | { 333 | super.init() 334 | intuGroupedArray = INTUMutableGroupedArray() 335 | } 336 | 337 | override public init(groupedArray: GroupedArray) 338 | { 339 | super.init() 340 | intuGroupedArray = INTUMutableGroupedArray(groupedArray: groupedArray.intuGroupedArray) 341 | } 342 | 343 | override public init(groupedArray: GroupedArray, copyItems: Bool) 344 | { 345 | super.init() 346 | intuGroupedArray = INTUMutableGroupedArray(groupedArray: groupedArray.intuGroupedArray, copyItems: copyItems) 347 | } 348 | 349 | override public init(array: [O]) 350 | { 351 | super.init() 352 | intuGroupedArray = INTUMutableGroupedArray(array: array) 353 | } 354 | 355 | public required init(arrayLiteral elements: AnyObject...) 356 | { 357 | super.init() 358 | intuGroupedArray = INTUMutableGroupedArray.literal(elements) 359 | } 360 | 361 | 362 | override subscript (sectionIndex: Int) -> S! { 363 | get { 364 | return sectionAtIndex(sectionIndex) 365 | } 366 | set(newValue) { 367 | replaceSectionAtIndex(sectionIndex, withSection: newValue) 368 | } 369 | } 370 | 371 | override subscript (sectionIndex: Int, objectIndex: Int) -> O! { 372 | get { 373 | return objectAtIndexPath(INTUGroupedArray.indexPathForRow(UInt(objectIndex), inSection: UInt(sectionIndex))) 374 | } 375 | set(newValue) { 376 | replaceObjectAtIndexPath(INTUGroupedArray.indexPathForRow(UInt(objectIndex), inSection: UInt(sectionIndex)), withObject: newValue) 377 | } 378 | } 379 | 380 | override subscript (indexPath: NSIndexPath) -> O! { 381 | get { 382 | return objectAtIndexPath(indexPath) 383 | } 384 | set(newValue) { 385 | replaceObjectAtIndexPath(indexPath, withObject: newValue) 386 | } 387 | } 388 | 389 | 390 | public func addObject(object: O, toSection section: S) 391 | { 392 | intuMutableGroupedArray.addObject(object, toSection: section) 393 | } 394 | 395 | public func addObject(object: O, toSection section: S, withSectionIndexHint sectionIndexHint: Int) 396 | { 397 | intuMutableGroupedArray.addObject(object, toSection: section, withSectionIndexHint: UInt(sectionIndexHint)) 398 | } 399 | 400 | public func addObject(object: O, toSectionAtIndex sectionIndex: Int) 401 | { 402 | intuMutableGroupedArray.addObject(object, toSectionAtIndex: UInt(sectionIndex)) 403 | } 404 | 405 | public func addObjectsFromArray(objects: [O], toSection section: S) 406 | { 407 | intuMutableGroupedArray.addObjectsFromArray(objects, toSection: section) 408 | } 409 | 410 | 411 | public func insertObject(object: O, atIndex index: Int, inSection section: S) 412 | { 413 | intuMutableGroupedArray.insertObject(object, atIndex: UInt(index), inSection: section) 414 | } 415 | 416 | public func insertObject(object: O, atIndexPath indexPath: NSIndexPath) 417 | { 418 | intuMutableGroupedArray.insertObject(object, atIndexPath: indexPath) 419 | } 420 | 421 | 422 | public func replaceSectionAtIndex(index: Int, withSection section: S) 423 | { 424 | intuMutableGroupedArray.replaceSectionAtIndex(UInt(index), withSection: section) 425 | } 426 | 427 | public func replaceObjectAtIndexPath(indexPath: NSIndexPath, withObject object: O) 428 | { 429 | intuMutableGroupedArray.replaceObjectAtIndexPath(indexPath, withObject: object) 430 | } 431 | 432 | 433 | public func moveSectionAtIndex(fromIndex: Int, toIndex: Int) 434 | { 435 | intuMutableGroupedArray.moveSectionAtIndex(UInt(fromIndex), toIndex: UInt(toIndex)) 436 | } 437 | 438 | public func moveObjectAtIndexPath(fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) 439 | { 440 | intuMutableGroupedArray.moveObjectAtIndexPath(fromIndexPath, toIndexPath: toIndexPath) 441 | } 442 | 443 | 444 | public func exchangeSectionAtIndex(index1: Int, withSectionAtIndex index2: Int) 445 | { 446 | intuMutableGroupedArray.exchangeSectionAtIndex(UInt(index1), withSectionAtIndex: UInt(index2)) 447 | } 448 | 449 | public func exchangeObjectAtIndexPath(indexPath1: NSIndexPath, withObjectAtIndexPath indexPath2: NSIndexPath) 450 | { 451 | intuMutableGroupedArray.exchangeObjectAtIndexPath(indexPath1, withObjectAtIndexPath: indexPath2) 452 | } 453 | 454 | 455 | public func removeAllObjects() 456 | { 457 | intuMutableGroupedArray.removeAllObjects() 458 | } 459 | 460 | public func removeSection(section: S) 461 | { 462 | intuMutableGroupedArray.removeSection(section) 463 | } 464 | 465 | public func removeSectionAtIndex(sectionIndex: Int) 466 | { 467 | intuMutableGroupedArray.removeSectionAtIndex(UInt(sectionIndex)) 468 | } 469 | 470 | public func removeObject(object: O) 471 | { 472 | intuMutableGroupedArray.removeObject(object) 473 | } 474 | 475 | public func removeObject(object: O, fromSection section: S) 476 | { 477 | intuMutableGroupedArray.removeObject(object, fromSection: section) 478 | } 479 | 480 | public func removeObjectAtIndex(index: Int, fromSection section: S) 481 | { 482 | intuMutableGroupedArray.removeObjectAtIndex(UInt(index), fromSection: section) 483 | } 484 | 485 | public func removeObjectAtIndexPath(indexPath: NSIndexPath) 486 | { 487 | intuMutableGroupedArray.removeObjectAtIndexPath(indexPath) 488 | } 489 | 490 | 491 | public func filter(sectionPredicate sectionPredicate: NSPredicate, objectPredicate: NSPredicate) 492 | { 493 | intuMutableGroupedArray.filterUsingSectionPredicate(sectionPredicate, objectPredicate: objectPredicate) 494 | } 495 | 496 | public func sort(sectionComparator sectionComparator: NSComparator, objectComparator: NSComparator) 497 | { 498 | intuMutableGroupedArray.sortUsingSectionComparator(sectionComparator, objectComparator: objectComparator) 499 | } 500 | } 501 | 502 | 503 | // MARK: Enumerators 504 | 505 | public class GroupedArraySectionEnumerator: SequenceType, GeneratorType 506 | { 507 | private var enumerator: NSEnumerator // Must also conform to INTUGroupedArraySectionEnumerator protocol 508 | 509 | public init(_ enumerator: NSEnumerator) 510 | { 511 | self.enumerator = enumerator 512 | } 513 | 514 | public func generate() -> GroupedArraySectionEnumerator 515 | { 516 | return self 517 | } 518 | 519 | public func next() -> S? 520 | { 521 | return nextSection() 522 | } 523 | 524 | public func nextSection() -> S? 525 | { 526 | let typedEnumerator = enumerator as! INTUGroupedArraySectionEnumerator 527 | return typedEnumerator.nextSection() as? S 528 | } 529 | 530 | public func allSections() -> [S] 531 | { 532 | let typedEnumerator = enumerator as! INTUGroupedArraySectionEnumerator 533 | return typedEnumerator.allSections() as! [S] 534 | } 535 | } 536 | 537 | public class GroupedArrayObjectEnumerator: SequenceType, GeneratorType 538 | { 539 | private var enumerator: NSEnumerator 540 | 541 | public init(_ enumerator: NSEnumerator) 542 | { 543 | self.enumerator = enumerator 544 | } 545 | 546 | public func generate() -> GroupedArrayObjectEnumerator 547 | { 548 | return self 549 | } 550 | 551 | public func next() -> O? 552 | { 553 | return nextObject() 554 | } 555 | 556 | public func nextObject() -> O? 557 | { 558 | return enumerator.nextObject() as? O 559 | } 560 | 561 | public func allObjects() -> [O] 562 | { 563 | return enumerator.allObjects as! [O] 564 | } 565 | } 566 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "INTUGroupedArrayImports.h" 6 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B14F25751A05E9F90067C976 /* INTUGroupedArraySectionContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = B14F256D1A05E9F90067C976 /* INTUGroupedArraySectionContainer.m */; }; 11 | B14F25761A05E9F90067C976 /* INTUGroupedArray.m in Sources */ = {isa = PBXBuildFile; fileRef = B14F25711A05E9F90067C976 /* INTUGroupedArray.m */; }; 12 | B14F25771A05E9F90067C976 /* INTUMutableGroupedArray.m in Sources */ = {isa = PBXBuildFile; fileRef = B14F25741A05E9F90067C976 /* INTUMutableGroupedArray.m */; }; 13 | B14F257A1A05EA560067C976 /* GroupedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F25791A05EA560067C976 /* GroupedArray.swift */; }; 14 | B1A6838D1A02DB8300C73235 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A6838C1A02DB8300C73235 /* AppDelegate.swift */; }; 15 | B1A6838F1A02DB8300C73235 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A6838E1A02DB8300C73235 /* ViewController.swift */; }; 16 | B1A683921A02DB8300C73235 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1A683901A02DB8300C73235 /* Main.storyboard */; }; 17 | B1A683941A02DB8300C73235 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B1A683931A02DB8300C73235 /* Images.xcassets */; }; 18 | B1A683971A02DB8300C73235 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1A683951A02DB8300C73235 /* LaunchScreen.xib */; }; 19 | B1A683A31A02DB8300C73235 /* SwiftGroupedArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A683A21A02DB8300C73235 /* SwiftGroupedArrayTests.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | B1A6839D1A02DB8300C73235 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = B1A6837F1A02DB8300C73235 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = B1A683861A02DB8300C73235; 28 | remoteInfo = SwiftGroupedArray; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | B14F256B1A05E9F90067C976 /* INTUGroupedArrayInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUGroupedArrayInternal.h; sourceTree = ""; }; 34 | B14F256C1A05E9F90067C976 /* INTUGroupedArraySectionContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUGroupedArraySectionContainer.h; sourceTree = ""; }; 35 | B14F256D1A05E9F90067C976 /* INTUGroupedArraySectionContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = INTUGroupedArraySectionContainer.m; sourceTree = ""; }; 36 | B14F256E1A05E9F90067C976 /* INTUIndexPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUIndexPair.h; sourceTree = ""; }; 37 | B14F256F1A05E9F90067C976 /* INTUMutableGroupedArrayInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUMutableGroupedArrayInternal.h; sourceTree = ""; }; 38 | B14F25701A05E9F90067C976 /* INTUGroupedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUGroupedArray.h; sourceTree = ""; }; 39 | B14F25711A05E9F90067C976 /* INTUGroupedArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = INTUGroupedArray.m; sourceTree = ""; }; 40 | B14F25721A05E9F90067C976 /* INTUGroupedArrayImports.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUGroupedArrayImports.h; sourceTree = ""; }; 41 | B14F25731A05E9F90067C976 /* INTUMutableGroupedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUMutableGroupedArray.h; sourceTree = ""; }; 42 | B14F25741A05E9F90067C976 /* INTUMutableGroupedArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = INTUMutableGroupedArray.m; sourceTree = ""; }; 43 | B14F25791A05EA560067C976 /* GroupedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GroupedArray.swift; path = ../../Source/Swift/GroupedArray.swift; sourceTree = ""; }; 44 | B1A683871A02DB8300C73235 /* SwiftGroupedArray.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftGroupedArray.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | B1A6838B1A02DB8300C73235 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | B1A6838C1A02DB8300C73235 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | B1A6838E1A02DB8300C73235 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 48 | B1A683911A02DB8300C73235 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | B1A683931A02DB8300C73235 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 50 | B1A683961A02DB8300C73235 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 51 | B1A6839C1A02DB8300C73235 /* SwiftGroupedArrayTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftGroupedArrayTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | B1A683A11A02DB8300C73235 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | B1A683A21A02DB8300C73235 /* SwiftGroupedArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftGroupedArrayTests.swift; sourceTree = ""; }; 54 | B1A683AC1A02DBA900C73235 /* SwiftGroupedArray-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftGroupedArray-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; 55 | B1A683AD1A02DBA900C73235 /* SwiftGroupedArrayTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftGroupedArrayTests-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; 56 | B1D1242D1B7A720E000282D2 /* INTUGroupedArrayDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = INTUGroupedArrayDefines.h; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | B1A683841A02DB8300C73235 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | B1A683991A02DB8300C73235 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | B14F25691A05E9F90067C976 /* INTUGroupedArray */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | B14F256A1A05E9F90067C976 /* Internal */, 81 | B14F25701A05E9F90067C976 /* INTUGroupedArray.h */, 82 | B14F25711A05E9F90067C976 /* INTUGroupedArray.m */, 83 | B14F25721A05E9F90067C976 /* INTUGroupedArrayImports.h */, 84 | B14F25731A05E9F90067C976 /* INTUMutableGroupedArray.h */, 85 | B14F25741A05E9F90067C976 /* INTUMutableGroupedArray.m */, 86 | ); 87 | name = INTUGroupedArray; 88 | path = ../Source/INTUGroupedArray; 89 | sourceTree = ""; 90 | }; 91 | B14F256A1A05E9F90067C976 /* Internal */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | B1D1242D1B7A720E000282D2 /* INTUGroupedArrayDefines.h */, 95 | B14F256B1A05E9F90067C976 /* INTUGroupedArrayInternal.h */, 96 | B14F256F1A05E9F90067C976 /* INTUMutableGroupedArrayInternal.h */, 97 | B14F256C1A05E9F90067C976 /* INTUGroupedArraySectionContainer.h */, 98 | B14F256D1A05E9F90067C976 /* INTUGroupedArraySectionContainer.m */, 99 | B14F256E1A05E9F90067C976 /* INTUIndexPair.h */, 100 | ); 101 | path = Internal; 102 | sourceTree = ""; 103 | }; 104 | B14F25781A05EA290067C976 /* Swift Wrapper */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | B14F25791A05EA560067C976 /* GroupedArray.swift */, 108 | ); 109 | name = "Swift Wrapper"; 110 | path = SwiftGroupedArray; 111 | sourceTree = ""; 112 | }; 113 | B1A6837E1A02DB8300C73235 = { 114 | isa = PBXGroup; 115 | children = ( 116 | B14F25691A05E9F90067C976 /* INTUGroupedArray */, 117 | B14F25781A05EA290067C976 /* Swift Wrapper */, 118 | B1A683891A02DB8300C73235 /* SwiftGroupedArray */, 119 | B1A6839F1A02DB8300C73235 /* SwiftGroupedArrayTests */, 120 | B1A683881A02DB8300C73235 /* Products */, 121 | ); 122 | sourceTree = ""; 123 | }; 124 | B1A683881A02DB8300C73235 /* Products */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | B1A683871A02DB8300C73235 /* SwiftGroupedArray.app */, 128 | B1A6839C1A02DB8300C73235 /* SwiftGroupedArrayTests.xctest */, 129 | ); 130 | name = Products; 131 | sourceTree = ""; 132 | }; 133 | B1A683891A02DB8300C73235 /* SwiftGroupedArray */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | B1A6838C1A02DB8300C73235 /* AppDelegate.swift */, 137 | B1A6838E1A02DB8300C73235 /* ViewController.swift */, 138 | B1A683901A02DB8300C73235 /* Main.storyboard */, 139 | B1A683931A02DB8300C73235 /* Images.xcassets */, 140 | B1A683951A02DB8300C73235 /* LaunchScreen.xib */, 141 | B1A683AC1A02DBA900C73235 /* SwiftGroupedArray-Bridging-Header.h */, 142 | B1A6838A1A02DB8300C73235 /* Supporting Files */, 143 | ); 144 | path = SwiftGroupedArray; 145 | sourceTree = ""; 146 | }; 147 | B1A6838A1A02DB8300C73235 /* Supporting Files */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | B1A6838B1A02DB8300C73235 /* Info.plist */, 151 | ); 152 | name = "Supporting Files"; 153 | sourceTree = ""; 154 | }; 155 | B1A6839F1A02DB8300C73235 /* SwiftGroupedArrayTests */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | B1A683AD1A02DBA900C73235 /* SwiftGroupedArrayTests-Bridging-Header.h */, 159 | B1A683A21A02DB8300C73235 /* SwiftGroupedArrayTests.swift */, 160 | B1A683A01A02DB8300C73235 /* Supporting Files */, 161 | ); 162 | path = SwiftGroupedArrayTests; 163 | sourceTree = ""; 164 | }; 165 | B1A683A01A02DB8300C73235 /* Supporting Files */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | B1A683A11A02DB8300C73235 /* Info.plist */, 169 | ); 170 | name = "Supporting Files"; 171 | sourceTree = ""; 172 | }; 173 | /* End PBXGroup section */ 174 | 175 | /* Begin PBXNativeTarget section */ 176 | B1A683861A02DB8300C73235 /* SwiftGroupedArray */ = { 177 | isa = PBXNativeTarget; 178 | buildConfigurationList = B1A683A61A02DB8300C73235 /* Build configuration list for PBXNativeTarget "SwiftGroupedArray" */; 179 | buildPhases = ( 180 | B1A683831A02DB8300C73235 /* Sources */, 181 | B1A683841A02DB8300C73235 /* Frameworks */, 182 | B1A683851A02DB8300C73235 /* Resources */, 183 | ); 184 | buildRules = ( 185 | ); 186 | dependencies = ( 187 | ); 188 | name = SwiftGroupedArray; 189 | productName = SwiftGroupedArray; 190 | productReference = B1A683871A02DB8300C73235 /* SwiftGroupedArray.app */; 191 | productType = "com.apple.product-type.application"; 192 | }; 193 | B1A6839B1A02DB8300C73235 /* SwiftGroupedArrayTests */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = B1A683A91A02DB8300C73235 /* Build configuration list for PBXNativeTarget "SwiftGroupedArrayTests" */; 196 | buildPhases = ( 197 | B1A683981A02DB8300C73235 /* Sources */, 198 | B1A683991A02DB8300C73235 /* Frameworks */, 199 | B1A6839A1A02DB8300C73235 /* Resources */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | B1A6839E1A02DB8300C73235 /* PBXTargetDependency */, 205 | ); 206 | name = SwiftGroupedArrayTests; 207 | productName = SwiftGroupedArrayTests; 208 | productReference = B1A6839C1A02DB8300C73235 /* SwiftGroupedArrayTests.xctest */; 209 | productType = "com.apple.product-type.bundle.unit-test"; 210 | }; 211 | /* End PBXNativeTarget section */ 212 | 213 | /* Begin PBXProject section */ 214 | B1A6837F1A02DB8300C73235 /* Project object */ = { 215 | isa = PBXProject; 216 | attributes = { 217 | LastSwiftUpdateCheck = 0700; 218 | LastUpgradeCheck = 0700; 219 | ORGANIZATIONNAME = Intuit; 220 | TargetAttributes = { 221 | B1A683861A02DB8300C73235 = { 222 | CreatedOnToolsVersion = 6.1; 223 | }; 224 | B1A6839B1A02DB8300C73235 = { 225 | CreatedOnToolsVersion = 6.1; 226 | TestTargetID = B1A683861A02DB8300C73235; 227 | }; 228 | }; 229 | }; 230 | buildConfigurationList = B1A683821A02DB8300C73235 /* Build configuration list for PBXProject "SwiftGroupedArray" */; 231 | compatibilityVersion = "Xcode 3.2"; 232 | developmentRegion = English; 233 | hasScannedForEncodings = 0; 234 | knownRegions = ( 235 | en, 236 | Base, 237 | ); 238 | mainGroup = B1A6837E1A02DB8300C73235; 239 | productRefGroup = B1A683881A02DB8300C73235 /* Products */; 240 | projectDirPath = ""; 241 | projectRoot = ""; 242 | targets = ( 243 | B1A683861A02DB8300C73235 /* SwiftGroupedArray */, 244 | B1A6839B1A02DB8300C73235 /* SwiftGroupedArrayTests */, 245 | ); 246 | }; 247 | /* End PBXProject section */ 248 | 249 | /* Begin PBXResourcesBuildPhase section */ 250 | B1A683851A02DB8300C73235 /* Resources */ = { 251 | isa = PBXResourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | B1A683921A02DB8300C73235 /* Main.storyboard in Resources */, 255 | B1A683971A02DB8300C73235 /* LaunchScreen.xib in Resources */, 256 | B1A683941A02DB8300C73235 /* Images.xcassets in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | B1A6839A1A02DB8300C73235 /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | /* End PBXResourcesBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | B1A683831A02DB8300C73235 /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | B14F257A1A05EA560067C976 /* GroupedArray.swift in Sources */, 275 | B1A6838F1A02DB8300C73235 /* ViewController.swift in Sources */, 276 | B1A6838D1A02DB8300C73235 /* AppDelegate.swift in Sources */, 277 | B14F25761A05E9F90067C976 /* INTUGroupedArray.m in Sources */, 278 | B14F25771A05E9F90067C976 /* INTUMutableGroupedArray.m in Sources */, 279 | B14F25751A05E9F90067C976 /* INTUGroupedArraySectionContainer.m in Sources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | B1A683981A02DB8300C73235 /* Sources */ = { 284 | isa = PBXSourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | B1A683A31A02DB8300C73235 /* SwiftGroupedArrayTests.swift in Sources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXSourcesBuildPhase section */ 292 | 293 | /* Begin PBXTargetDependency section */ 294 | B1A6839E1A02DB8300C73235 /* PBXTargetDependency */ = { 295 | isa = PBXTargetDependency; 296 | target = B1A683861A02DB8300C73235 /* SwiftGroupedArray */; 297 | targetProxy = B1A6839D1A02DB8300C73235 /* PBXContainerItemProxy */; 298 | }; 299 | /* End PBXTargetDependency section */ 300 | 301 | /* Begin PBXVariantGroup section */ 302 | B1A683901A02DB8300C73235 /* Main.storyboard */ = { 303 | isa = PBXVariantGroup; 304 | children = ( 305 | B1A683911A02DB8300C73235 /* Base */, 306 | ); 307 | name = Main.storyboard; 308 | sourceTree = ""; 309 | }; 310 | B1A683951A02DB8300C73235 /* LaunchScreen.xib */ = { 311 | isa = PBXVariantGroup; 312 | children = ( 313 | B1A683961A02DB8300C73235 /* Base */, 314 | ); 315 | name = LaunchScreen.xib; 316 | sourceTree = ""; 317 | }; 318 | /* End PBXVariantGroup section */ 319 | 320 | /* Begin XCBuildConfiguration section */ 321 | B1A683A41A02DB8300C73235 /* Debug */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ALWAYS_SEARCH_USER_PATHS = NO; 325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 326 | CLANG_CXX_LIBRARY = "libc++"; 327 | CLANG_ENABLE_MODULES = YES; 328 | CLANG_ENABLE_OBJC_ARC = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_CONSTANT_CONVERSION = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_EMPTY_BODY = YES; 333 | CLANG_WARN_ENUM_CONVERSION = YES; 334 | CLANG_WARN_INT_CONVERSION = YES; 335 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | ENABLE_STRICT_OBJC_MSGSEND = YES; 341 | ENABLE_TESTABILITY = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_DYNAMIC_NO_PIC = NO; 344 | GCC_OPTIMIZATION_LEVEL = 0; 345 | GCC_PREPROCESSOR_DEFINITIONS = ( 346 | "DEBUG=1", 347 | "$(inherited)", 348 | ); 349 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 352 | GCC_WARN_UNDECLARED_SELECTOR = YES; 353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 354 | GCC_WARN_UNUSED_FUNCTION = YES; 355 | GCC_WARN_UNUSED_VARIABLE = YES; 356 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 357 | MTL_ENABLE_DEBUG_INFO = YES; 358 | ONLY_ACTIVE_ARCH = YES; 359 | SDKROOT = iphoneos; 360 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | }; 363 | name = Debug; 364 | }; 365 | B1A683A51A02DB8300C73235 /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ALWAYS_SEARCH_USER_PATHS = NO; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BOOL_CONVERSION = YES; 374 | CLANG_WARN_CONSTANT_CONVERSION = YES; 375 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 376 | CLANG_WARN_EMPTY_BODY = YES; 377 | CLANG_WARN_ENUM_CONVERSION = YES; 378 | CLANG_WARN_INT_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_UNREACHABLE_CODE = YES; 381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 382 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 383 | COPY_PHASE_STRIP = YES; 384 | ENABLE_NS_ASSERTIONS = NO; 385 | ENABLE_STRICT_OBJC_MSGSEND = YES; 386 | GCC_C_LANGUAGE_STANDARD = gnu99; 387 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 388 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 389 | GCC_WARN_UNDECLARED_SELECTOR = YES; 390 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 391 | GCC_WARN_UNUSED_FUNCTION = YES; 392 | GCC_WARN_UNUSED_VARIABLE = YES; 393 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 394 | MTL_ENABLE_DEBUG_INFO = NO; 395 | SDKROOT = iphoneos; 396 | TARGETED_DEVICE_FAMILY = "1,2"; 397 | VALIDATE_PRODUCT = YES; 398 | }; 399 | name = Release; 400 | }; 401 | B1A683A71A02DB8300C73235 /* Debug */ = { 402 | isa = XCBuildConfiguration; 403 | buildSettings = { 404 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 405 | CLANG_ENABLE_MODULES = YES; 406 | INFOPLIST_FILE = SwiftGroupedArray/Info.plist; 407 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 408 | PRODUCT_BUNDLE_IDENTIFIER = "com.intuit.$(PRODUCT_NAME:rfc1034identifier)"; 409 | PRODUCT_NAME = "$(TARGET_NAME)"; 410 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftGroupedArray-Bridging-Header.h"; 411 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 412 | }; 413 | name = Debug; 414 | }; 415 | B1A683A81A02DB8300C73235 /* Release */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 419 | CLANG_ENABLE_MODULES = YES; 420 | INFOPLIST_FILE = SwiftGroupedArray/Info.plist; 421 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 422 | PRODUCT_BUNDLE_IDENTIFIER = "com.intuit.$(PRODUCT_NAME:rfc1034identifier)"; 423 | PRODUCT_NAME = "$(TARGET_NAME)"; 424 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftGroupedArray-Bridging-Header.h"; 425 | }; 426 | name = Release; 427 | }; 428 | B1A683AA1A02DB8300C73235 /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | BUNDLE_LOADER = "$(TEST_HOST)"; 432 | CLANG_ENABLE_MODULES = YES; 433 | FRAMEWORK_SEARCH_PATHS = ( 434 | "$(SDKROOT)/Developer/Library/Frameworks", 435 | "$(inherited)", 436 | ); 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | INFOPLIST_FILE = SwiftGroupedArrayTests/Info.plist; 442 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 443 | PRODUCT_BUNDLE_IDENTIFIER = "com.intuit.$(PRODUCT_NAME:rfc1034identifier)"; 444 | PRODUCT_NAME = "$(TARGET_NAME)"; 445 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftGroupedArrayTests-Bridging-Header.h"; 446 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 447 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftGroupedArray.app/SwiftGroupedArray"; 448 | }; 449 | name = Debug; 450 | }; 451 | B1A683AB1A02DB8300C73235 /* Release */ = { 452 | isa = XCBuildConfiguration; 453 | buildSettings = { 454 | BUNDLE_LOADER = "$(TEST_HOST)"; 455 | CLANG_ENABLE_MODULES = YES; 456 | FRAMEWORK_SEARCH_PATHS = ( 457 | "$(SDKROOT)/Developer/Library/Frameworks", 458 | "$(inherited)", 459 | ); 460 | INFOPLIST_FILE = SwiftGroupedArrayTests/Info.plist; 461 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 462 | PRODUCT_BUNDLE_IDENTIFIER = "com.intuit.$(PRODUCT_NAME:rfc1034identifier)"; 463 | PRODUCT_NAME = "$(TARGET_NAME)"; 464 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftGroupedArrayTests-Bridging-Header.h"; 465 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftGroupedArray.app/SwiftGroupedArray"; 466 | }; 467 | name = Release; 468 | }; 469 | /* End XCBuildConfiguration section */ 470 | 471 | /* Begin XCConfigurationList section */ 472 | B1A683821A02DB8300C73235 /* Build configuration list for PBXProject "SwiftGroupedArray" */ = { 473 | isa = XCConfigurationList; 474 | buildConfigurations = ( 475 | B1A683A41A02DB8300C73235 /* Debug */, 476 | B1A683A51A02DB8300C73235 /* Release */, 477 | ); 478 | defaultConfigurationIsVisible = 0; 479 | defaultConfigurationName = Release; 480 | }; 481 | B1A683A61A02DB8300C73235 /* Build configuration list for PBXNativeTarget "SwiftGroupedArray" */ = { 482 | isa = XCConfigurationList; 483 | buildConfigurations = ( 484 | B1A683A71A02DB8300C73235 /* Debug */, 485 | B1A683A81A02DB8300C73235 /* Release */, 486 | ); 487 | defaultConfigurationIsVisible = 0; 488 | defaultConfigurationName = Release; 489 | }; 490 | B1A683A91A02DB8300C73235 /* Build configuration list for PBXNativeTarget "SwiftGroupedArrayTests" */ = { 491 | isa = XCConfigurationList; 492 | buildConfigurations = ( 493 | B1A683AA1A02DB8300C73235 /* Debug */, 494 | B1A683AB1A02DB8300C73235 /* Release */, 495 | ); 496 | defaultConfigurationIsVisible = 0; 497 | defaultConfigurationName = Release; 498 | }; 499 | /* End XCConfigurationList section */ 500 | }; 501 | rootObject = B1A6837F1A02DB8300C73235 /* Project object */; 502 | } 503 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | @UIApplicationMain 30 | class AppDelegate: UIResponder, UIApplicationDelegate 31 | { 32 | var window: UIWindow? 33 | 34 | 35 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool 36 | { 37 | // Override point for customization after application launch. 38 | return true 39 | } 40 | 41 | func applicationWillResignActive(application: UIApplication) 42 | { 43 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 44 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 45 | } 46 | 47 | func applicationDidEnterBackground(application: UIApplication) 48 | { 49 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | } 52 | 53 | func applicationWillEnterForeground(application: UIApplication) 54 | { 55 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 56 | } 57 | 58 | func applicationDidBecomeActive(application: UIApplication) 59 | { 60 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 61 | } 62 | 63 | func applicationWillTerminate(application: UIApplication) 64 | { 65 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 66 | } 67 | 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArray/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | class ViewController: UIViewController 30 | { 31 | override func viewDidLoad() 32 | { 33 | super.viewDidLoad() 34 | 35 | groupedArrayDemos() 36 | 37 | moreGroupedArrayDemos() 38 | } 39 | 40 | func groupedArrayDemos() 41 | { 42 | // Create a grouped array using the array literal syntax 43 | let groupedArray: GroupedArray = ["Section 1", ["Object A", "Object B", "Object C"], 44 | "Section 2", ["Object D"], 45 | "Section 3", ["Object E", "Object F"], 46 | "Section 4", ["Object G"]] 47 | 48 | // Access a section and object 49 | let section0 = groupedArray.sectionAtIndex(0) 50 | print("The first section is: \(section0)") 51 | let object00 = groupedArray.objectAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) 52 | print("The first object is: \(object00)") 53 | 54 | // Use some APIs that return optionals 55 | if let sectionIndex = groupedArray.indexOfSection("Section 4") { 56 | print("Found the section at index: \(sectionIndex)") 57 | } 58 | if let lastObject = groupedArray.lastObject() { 59 | print("The last object is: \(lastObject)") 60 | } 61 | 62 | 63 | // Iterate over the grouped array 64 | for (section, object) in groupedArray { 65 | print("Enumerating with tuple (section: \(section), object: \(object))") 66 | } 67 | 68 | // Iterate over just the sections 69 | for section in groupedArray.sectionEnumerator() { 70 | print("Enumerating section: \(section)") 71 | } 72 | 73 | // Iterate over the objects in reverse 74 | for object in groupedArray.reverseObjectEnumerator() { 75 | print("Enumerating reversed object: \(object)") 76 | } 77 | 78 | 79 | // Add objects to a mutable grouped array 80 | var mutableGroupedArray = MutableGroupedArray() 81 | mutableGroupedArray.addObject("Object A", toSection: "Section 1") 82 | mutableGroupedArray.addObject("Object B", toSection: "Section 1") 83 | mutableGroupedArray.addObject("Object C", toSection: "Section 1") 84 | mutableGroupedArray.addObject("Object D", toSection: "Section 2") 85 | mutableGroupedArray.addObject("Object E", toSection: "Section 3") 86 | mutableGroupedArray.addObject("Object F", toSection: "Section 3") 87 | mutableGroupedArray.addObject("Object G", toSection: "Section 4") 88 | 89 | 90 | // Test equality using the == operator 91 | print("Are the two grouped arrays equal: \(groupedArray == mutableGroupedArray)") 92 | 93 | 94 | // Use subscripts to access a section by index 95 | let section = mutableGroupedArray[0] 96 | print("The first section is: \(section)") 97 | // Use subscripts to access an object passing in a section index and object index 98 | let object = mutableGroupedArray[0, 1] 99 | print("The second object in the first section is: \(object)") 100 | // Use subscripts to access an object by index path 101 | let indexPath = NSIndexPath(forRow: 2, inSection: 0) 102 | let anotherObject = mutableGroupedArray[indexPath] 103 | print("The third object in the first section is: \(anotherObject)") 104 | 105 | // Replace a section with a new section using subscripts 106 | mutableGroupedArray[1] = "New Section 2" 107 | print("The second section is now: \(mutableGroupedArray[1])") 108 | // Replace some objects with new objects using subscripts 109 | mutableGroupedArray[1, 0] = "New Object D" 110 | print("The object in the second section is now: \(mutableGroupedArray[1, 0])") 111 | mutableGroupedArray[indexPath] = "New Object C" 112 | print("The last object in the first section is now: \(mutableGroupedArray[indexPath])") 113 | } 114 | 115 | func moreGroupedArrayDemos() 116 | { 117 | // When using the array literal syntax, if the type is not explicitly annotated, it will default to a Swift Array 118 | let a = [5, ["Some text"], 10, ["Some more text"]] 119 | print(a) 120 | // Same literal, but with the GroupedArray type (and generics) made explicit 121 | let b: GroupedArray = [5, ["Some text"], 10, ["Some more text"]] 122 | print(b) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArrayTests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArrayTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftGroupedArray/SwiftGroupedArrayTests/SwiftGroupedArrayTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftGroupedArrayTests.swift 3 | // https://github.com/intuit/GroupedArray 4 | // 5 | // Copyright (c) 2014-2015 Intuit Inc. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | import XCTest 29 | 30 | class SwiftGroupedArrayTests: XCTestCase { 31 | 32 | override func setUp() { 33 | super.setUp() 34 | // Put setup code here. This method is called before the invocation of each test method in the class. 35 | } 36 | 37 | override func tearDown() { 38 | // Put teardown code here. This method is called after the invocation of each test method in the class. 39 | super.tearDown() 40 | } 41 | 42 | func testExample() { 43 | // This is an example of a functional test case. 44 | XCTAssert(true, "Pass") 45 | } 46 | 47 | func testPerformanceExample() { 48 | // This is an example of a performance test case. 49 | self.measureBlock() { 50 | // Put the code you want to measure the time of here. 51 | } 52 | } 53 | 54 | } 55 | --------------------------------------------------------------------------------