Privet Drive,4 Little Whinging Surrey England Great Britain
"
+ //"\n" + "
Harry Potter's House
\n" + "
\n" + "Privet Drive, 4 Little Whinging Surrey England Great Britain\n" + "
\n" + ""
+ message2.text = "Vestibulum id ligula porta felis euismod semper. Morbi leo risus, porta ac consectetur ac, vestibulum at eros."
+
+ let media = Media()
+ media.size = CGSize(width: 1180, height: 596)
+ media.url = URL(string:"https://media.wazirx.com/test_resources/crypto_gifts.png")
+
+ let bullet2 = Bullet()
+ bullet2.bulletType = .unicode
+ bullet2.unicode = "1f44d"
+
+ let bulletPoint21 = BulletPoint()
+ bulletPoint21.bullet = bullet2
+ bulletPoint21.titleText = "Vestibulum"
+ bulletPoint21.subTitleText = "Etiam porta sem malesuada magna mollis euismod."
+
+ let bulletPoint22 = BulletPoint()
+ bulletPoint22.bullet = bullet2
+ bulletPoint22.titleText = "Justo Condimentum"
+ bulletPoint22.subTitleText = "Sed posuere consectetur est at lobortis. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor."
+
+ let actionButton = ActionButton()
+ actionButton.title = "Take me to Crypto Gifts"
+
+ // Create Items List
+ let bulletItems2 = [title2, message2, actionButton,media, bulletPoint21]// [title2, message2, bulletPoint21, bulletPoint22]
+
+ // Register Item
+ dataStore.registerVersionInfo(version: version, items: bulletItems2)
+ }
+
+ @IBAction func bulletinButtonTapped(_ sender: Any) {
+
+ // Creage DataStore Object
+ let dataSource = BulletinDataStore()
+
+ // Register Multiple Versions
+ registerBulletingDetails(forVersion: Version("1.11"), inDataStore: dataSource)
+ registerBulletingDetails(forVersion: Version("1.12"), inDataStore: dataSource)
+
+ let sdk = BulletinSDK(dataStore: dataSource,appearance: Appearance.darkKnight)
+ let bulletinVC = sdk.getFullBulletin()
+
+ // Get Top Most View Controller
+ BulletinHelper.topMostViewController { (topMostVC) in
+
+ // Validation
+ guard let topMostVC = topMostVC else {
+ return
+ }
+
+ guard let bulletinVC = bulletinVC else {
+ return
+ }
+
+ bulletinVC.delegate = self
+
+ bulletinVC.loadViewIfNeeded()
+ bulletinVC.reload(animated: false,completion: {_ in
+ // Display Bulletin PopUp VC
+ let segue = AlertSegue(identifier: nil, source: topMostVC, destination: bulletinVC)
+ topMostVC.prepare(for: segue, sender: nil)
+ segue.perform()
+ })
+ }
+
+ }
+
+}
+
+//// MARK: - WZSettingsSectionControllerDelegate
+extension ViewController: BulletinListDelegate {
+
+ func BulletinList(didClickItem item: BulletinItem) {
+
+ // Validation
+ if let actionButtonItem = item as? ActionButton,
+ let actionButtonPayload = actionButtonItem.clickPayload {
+ print(actionButtonPayload)
+ }
+
+ }
+
+}
+
diff --git a/Demo/Podfile b/Demo/Podfile
new file mode 100644
index 0000000..05ac606
--- /dev/null
+++ b/Demo/Podfile
@@ -0,0 +1,13 @@
+# Uncomment the next line to define a global platform for your project
+# platform :ios, '9.0'
+
+target 'Bulletin' do
+ # Comment the next line if you don't want to use dynamic frameworks
+ use_frameworks!
+
+ # Pods for Bulletin
+ pod 'UIColor_Hex_Swift'
+ pod 'IGListKit'
+ pod 'Kingfisher','~> 7.6.2'
+ pod 'SwiftMessages'
+end
diff --git a/Demo/Podfile.lock b/Demo/Podfile.lock
new file mode 100644
index 0000000..8a29c14
--- /dev/null
+++ b/Demo/Podfile.lock
@@ -0,0 +1,34 @@
+PODS:
+ - IGListDiffKit (4.0.0)
+ - IGListKit (4.0.0):
+ - IGListDiffKit (= 4.0.0)
+ - Kingfisher (7.6.2)
+ - SwiftMessages (9.0.6):
+ - SwiftMessages/App (= 9.0.6)
+ - SwiftMessages/App (9.0.6)
+ - UIColor_Hex_Swift (5.1.7)
+
+DEPENDENCIES:
+ - IGListKit
+ - Kingfisher (~> 7.6.2)
+ - SwiftMessages
+ - UIColor_Hex_Swift
+
+SPEC REPOS:
+ trunk:
+ - IGListDiffKit
+ - IGListKit
+ - Kingfisher
+ - SwiftMessages
+ - UIColor_Hex_Swift
+
+SPEC CHECKSUMS:
+ IGListDiffKit: 665d6cf43ce726e676013db9c7d6c4294259b6b2
+ IGListKit: fd5a5d21935298f5849fa49d426843cff97b77c7
+ Kingfisher: 6c5449c6450c5239166510ba04afe374a98afc4f
+ SwiftMessages: f0c7ef4705a570ad6c5e208b611f4333e660ed92
+ UIColor_Hex_Swift: 31cd3e47440f07a20d2503a36bb0437a445b3c29
+
+PODFILE CHECKSUM: 310645f0fa108e1099503961f2d58481962552ed
+
+COCOAPODS: 1.12.1
diff --git a/Demo/Pods/IGListDiffKit/LICENSE.md b/Demo/Pods/IGListDiffKit/LICENSE.md
new file mode 100755
index 0000000..87cbf53
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) Facebook, Inc. and its affiliates.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/Demo/Pods/IGListDiffKit/README.md b/Demo/Pods/IGListDiffKit/README.md
new file mode 100644
index 0000000..22981eb
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/README.md
@@ -0,0 +1,110 @@
+
+
+----------------
+
+A data-driven `UICollectionView` framework for building fast and flexible lists.
+
+| | Main Features |
+----------|-----------------
+🙅 | Never call `performBatchUpdates(_:, completion:)` or `reloadData()` again
+🏠 | Better architecture with reusable cells and components
+🔠 | Create collections with multiple data types
+🔑 | Decoupled diffing algorithm
+✅ | Fully unit tested
+🔍 | Customize your diffing behavior for your models
+📱 | Simply `UICollectionView` at its core
+🚀 | Extendable API
+🐦 | Written in Objective-C with full Swift interop support
+
+`IGListKit` is built and maintained with ❤️ by [Instagram engineering](https://engineering.instagram.com/).
+We use the open source version `master` branch in the Instagram app.
+
+## Requirements
+
+- Xcode 9.0+
+- iOS 9.0+
+- tvOS 9.0+
+- macOS 10.11+ *(diffing algorithm components only)*
+- Interoperability with Swift 3.0+
+
+## Installation
+
+### CocoaPods
+
+The preferred installation method is with [CocoaPods](https://cocoapods.org). Add the following to your `Podfile`:
+
+```ruby
+pod 'IGListKit', '~> 4.0.0'
+```
+
+### Carthage
+
+For [Carthage](https://github.com/Carthage/Carthage), add the following to your `Cartfile`:
+
+```ogdl
+github "Instagram/IGListKit" ~> 4.0.0
+```
+
+> For advanced usage, see our [Installation Guide](https://instagram.github.io/IGListKit/installation.html).
+
+## Getting Started
+
+```bash
+$ git clone https://github.com/Instagram/IGListKit.git
+$ cd IGListKit/
+$ ./scripts/setup.sh
+```
+
+- Our [Getting Started guide](https://instagram.github.io/IGListKit/getting-started.html)
+- Ray Wenderlich's [IGListKit Tutorial: Better UICollectionViews](https://www.raywenderlich.com/147162/iglistkit-tutorial-better-uicollectionviews)
+- Our [example projects](https://github.com/Instagram/IGListKit/tree/master/Examples)
+- Ryan Nystrom's [talk at try! Swift NYC](https://realm.io/news/tryswift-ryan-nystrom-refactoring-at-scale-lessons-learned-rewriting-instagram-feed/) (Note: this talk was for an earlier version. Some APIs have changed.)
+- [Migrating an UITableView to IGListCollectionView](https://medium.com/cocoaacademymag/iglistkit-migrating-an-uitableview-to-iglistkitcollectionview-65a30cf9bac9), by Rodrigo Cavalcante
+- [Keeping data fresh in Buffer for iOS with AsyncDisplayKit, IGListKit & Pusher](https://overflow.buffer.com/2017/04/10/keeping-data-fresh-buffer-ios-asyncdisplaykit-iglistkit-pusher/), Andy Yates, Buffer
+
+## Documentation
+
+You can find [the docs here](https://instagram.github.io/IGListKit). Documentation is generated with [jazzy](https://github.com/realm/jazzy) and hosted on [GitHub-Pages](https://pages.github.com).
+
+To regenerate docs, run `./scripts/build_docs.sh` from the root directory in the repo.
+
+## Vision
+
+For the long-term goals and "vision" of `IGListKit`, please read our [Vision](https://github.com/Instagram/IGListKit/blob/master/Guides/VISION.md) doc.
+
+## Contributing
+
+Please see the [CONTRIBUTING](https://github.com/Instagram/IGListKit/blob/master/.github/CONTRIBUTING.md) file for how to help. At Instagram, we sync the open source version of `IGListKit` daily, so we're always testing the latest changes. But that requires all changes be thoroughly tested and follow our style guide.
+
+We have a set of [starter tasks](https://github.com/Instagram/IGListKit/issues?q=is%3Aissue+is%3Aopen+label%3Astarter-task) that are great for beginners to jump in on and start contributing.
+
+## License
+
+`IGListKit` is [MIT-licensed](./LICENSE).
+
+The files in the `/Examples/` directory are licensed under a separate license as specified in each file. Documentation is licensed [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/).
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListAssert.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListAssert.h
new file mode 100644
index 0000000..97466e2
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListAssert.h
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#ifndef IGAssert
+#define IGAssert( condition, ... ) NSCAssert( (condition) , ##__VA_ARGS__)
+#endif // IGAssert
+
+#ifndef IGFailAssert
+#define IGFailAssert( ... ) IGAssert( (NO) , ##__VA_ARGS__)
+#endif // IGFailAssert
+
+#ifndef IGParameterAssert
+#define IGParameterAssert( condition ) IGAssert( (condition) , @"Invalid parameter not satisfying: %@", @#condition)
+#endif // IGParameterAssert
+
+#ifndef IGAssertMainThread
+#define IGAssertMainThread() IGAssert( ([NSThread isMainThread] == YES), @"Must be on the main thread")
+#endif // IGAssertMainThread
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListBatchUpdateData.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListBatchUpdateData.h
new file mode 100644
index 0000000..4482d9b
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListBatchUpdateData.h
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#import
+#import
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ An instance of `IGListBatchUpdateData` takes section indexes and item index paths
+ and performs cleanup on init in order to perform a crash-free
+ update via `-[UICollectionView performBatchUpdates:completion:]`.
+ */
+IGLK_SUBCLASSING_RESTRICTED
+NS_SWIFT_NAME(ListBatchUpdateData)
+@interface IGListBatchUpdateData : NSObject
+
+/**
+ Section insert indexes.
+ */
+@property (nonatomic, strong, readonly) NSIndexSet *insertSections;
+
+/**
+ Section delete indexes.
+ */
+@property (nonatomic, strong, readonly) NSIndexSet *deleteSections;
+
+/**
+ Section moves.
+ */
+@property (nonatomic, strong, readonly) NSSet *moveSections;
+
+/**
+ Item insert index paths.
+ */
+@property (nonatomic, strong, readonly) NSArray *insertIndexPaths;
+
+/**
+ Item delete index paths.
+ */
+@property (nonatomic, strong, readonly) NSArray *deleteIndexPaths;
+
+/**
+ Item update index paths.
+ */
+@property (nonatomic, strong, readonly) NSArray *updateIndexPaths;
+
+/**
+ Item moves.
+ */
+@property (nonatomic, strong, readonly) NSArray *moveIndexPaths;
+
+/**
+ Creates a new batch update object with section and item operations.
+
+ @param insertSections Section indexes to insert.
+ @param deleteSections Section indexes to delete.
+ @param moveSections Section moves.
+ @param insertIndexPaths Item index paths to insert.
+ @param deleteIndexPaths Item index paths to delete.
+ @param updateIndexPaths Item index paths to update.
+ @param moveIndexPaths Item index paths to move.
+ @param fixIndexPathImbalance When enabled, we remove duplicate NSIndexPath inserts to avoid insert/delete imbalance and a crash.
+
+ @return A new batch update object.
+ */
+- (instancetype)initWithInsertSections:(NSIndexSet *)insertSections
+ deleteSections:(NSIndexSet *)deleteSections
+ moveSections:(NSSet *)moveSections
+ insertIndexPaths:(NSArray *)insertIndexPaths
+ deleteIndexPaths:(NSArray *)deleteIndexPaths
+ updateIndexPaths:(NSArray *)updateIndexPaths
+ moveIndexPaths:(NSArray *)moveIndexPaths
+ fixIndexPathImbalance:(BOOL)fixIndexPathImbalance NS_DESIGNATED_INITIALIZER;
+
+/**
+ Convenience initializer with fixIndexPathImbalance disabled.
+ */
+- (instancetype)initWithInsertSections:(NSIndexSet *)insertSections
+ deleteSections:(NSIndexSet *)deleteSections
+ moveSections:(NSSet *)moveSections
+ insertIndexPaths:(NSArray *)insertIndexPaths
+ deleteIndexPaths:(NSArray *)deleteIndexPaths
+ updateIndexPaths:(NSArray *)updateIndexPaths
+ moveIndexPaths:(NSArray *)moveIndexPaths;
+
+/**
+ :nodoc:
+ */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ :nodoc:
+ */
++ (instancetype)new NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListBatchUpdateData.mm b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListBatchUpdateData.mm
new file mode 100644
index 0000000..7bdf32b
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListBatchUpdateData.mm
@@ -0,0 +1,179 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "IGListBatchUpdateData.h"
+
+#import
+
+#import
+#import
+
+// Plucks the given move from available moves and turns it into a delete + insert
+static void convertMoveToDeleteAndInsert(NSMutableSet *moves,
+ IGListMoveIndex *move,
+ NSMutableIndexSet *deletes,
+ NSMutableIndexSet *inserts) {
+ [moves removeObject:move];
+
+ // add a delete and insert respecting the move's from and to sections
+ // delete + insert will result in reloading the entire section
+ [deletes addIndex:move.from];
+ [inserts addIndex:move.to];
+}
+
+@implementation IGListBatchUpdateData
+
+// Converts all section moves that have index path operations into a section delete + insert.
++ (void)_cleanIndexPathsWithMap:(const std::unordered_map &)map
+ moves:(NSMutableSet *)moves
+ indexPaths:(NSMutableArray *)indexPaths
+ deletes:(NSMutableIndexSet *)deletes
+ inserts:(NSMutableIndexSet *)inserts {
+ for (NSInteger i = indexPaths.count - 1; i >= 0; i--) {
+ NSIndexPath *path = indexPaths[i];
+ const auto it = map.find(path.section);
+ if (it != map.end() && it->second != nil) {
+ [indexPaths removeObjectAtIndex:i];
+ convertMoveToDeleteAndInsert(moves, it->second, deletes, inserts);
+ }
+ }
+}
+
+- (instancetype)initWithInsertSections:(NSIndexSet *)insertSections
+ deleteSections:(NSIndexSet *)deleteSections
+ moveSections:(NSSet *)moveSections
+ insertIndexPaths:(NSArray *)insertIndexPaths
+ deleteIndexPaths:(NSArray *)deleteIndexPaths
+ updateIndexPaths:(NSArray *)updateIndexPaths
+ moveIndexPaths:(NSArray *)moveIndexPaths {
+ return [self initWithInsertSections:insertSections
+ deleteSections:deleteSections
+ moveSections:moveSections
+ insertIndexPaths:insertIndexPaths
+ deleteIndexPaths:deleteIndexPaths
+ updateIndexPaths:updateIndexPaths
+ moveIndexPaths:moveIndexPaths
+ fixIndexPathImbalance:NO];
+}
+
+/**
+ Converts all section moves that are also reloaded, or have index path inserts, deletes, or reloads into a section
+ delete + insert in order to avoid UICollectionView heap corruptions, exceptions, and animation/snapshot bugs.
+ */
+- (instancetype)initWithInsertSections:(nonnull NSIndexSet *)insertSections
+ deleteSections:(nonnull NSIndexSet *)deleteSections
+ moveSections:(nonnull NSSet *)moveSections
+ insertIndexPaths:(nonnull NSArray *)insertIndexPaths
+ deleteIndexPaths:(nonnull NSArray *)deleteIndexPaths
+ updateIndexPaths:(nonnull NSArray *)updateIndexPaths
+ moveIndexPaths:(nonnull NSArray *)moveIndexPaths
+ fixIndexPathImbalance:(BOOL)fixIndexPathImbalance {
+ IGParameterAssert(insertSections != nil);
+ IGParameterAssert(deleteSections != nil);
+ IGParameterAssert(moveSections != nil);
+ IGParameterAssert(insertIndexPaths != nil);
+ IGParameterAssert(deleteIndexPaths != nil);
+ IGParameterAssert(updateIndexPaths != nil);
+ IGParameterAssert(moveIndexPaths != nil);
+ if (self = [super init]) {
+ NSMutableSet *mMoveSections = [moveSections mutableCopy];
+ NSMutableIndexSet *mDeleteSections = [deleteSections mutableCopy];
+ NSMutableIndexSet *mInsertSections = [insertSections mutableCopy];
+ NSMutableSet *mMoveIndexPaths = [moveIndexPaths mutableCopy];
+
+ // these collections should NEVER be mutated during cleanup passes, otherwise sections that have multiple item
+ // changes (e.g. a moved section that has a delete + reload on different index paths w/in the section) will only
+ // convert one of the item changes into a section delete+insert. this will fail hard and be VERY difficult to
+ // debug
+ const NSInteger moveCount = [moveSections count];
+ std::unordered_map fromMap(moveCount);
+ std::unordered_map toMap(moveCount);
+ for (IGListMoveIndex *move in moveSections) {
+ const NSInteger from = move.from;
+ const NSInteger to = move.to;
+
+ // if the move is already deleted or inserted, discard it because count-changing operations must match
+ // with data source changes
+ if ([deleteSections containsIndex:from] || [insertSections containsIndex:to]) {
+ [mMoveSections removeObject:move];
+ } else {
+ fromMap[from] = move;
+ toMap[to] = move;
+ }
+ }
+
+ // avoid a flaky UICollectionView bug when deleting from the same index path twice
+ // exposes a possible data source inconsistency issue
+ NSMutableArray *mDeleteIndexPaths = [[[NSSet setWithArray:deleteIndexPaths] allObjects] mutableCopy];
+
+ NSMutableArray *mInsertIndexPaths;
+ if (fixIndexPathImbalance) {
+ // Since we remove duplicate deletes (see above) we also need to remove inserts to keep the same insert/delete
+ // balance. For example, if we reload (insert & delete) the same NSIndexPath twice, we would otherwise end up
+ // with 2 inserts and 1 delete.
+ mInsertIndexPaths = [[[NSSet setWithArray:insertIndexPaths] allObjects] mutableCopy];
+ } else {
+ mInsertIndexPaths = [insertIndexPaths mutableCopy];
+ }
+
+ // avoids a bug where a cell is animated twice and one of the snapshot cells is never removed from the hierarchy
+ [IGListBatchUpdateData _cleanIndexPathsWithMap:fromMap moves:mMoveSections indexPaths:mDeleteIndexPaths deletes:mDeleteSections inserts:mInsertSections];
+
+ // prevents a bug where UICollectionView corrupts the heap memory when inserting into a section that is moved
+ [IGListBatchUpdateData _cleanIndexPathsWithMap:toMap moves:mMoveSections indexPaths:mInsertIndexPaths deletes:mDeleteSections inserts:mInsertSections];
+
+ for (IGListMoveIndexPath *move in moveIndexPaths) {
+ // if the section w/ an index path move is deleted, just drop the move
+ if ([deleteSections containsIndex:move.from.section]) {
+ [mMoveIndexPaths removeObject:move];
+ }
+
+ // if a move is inside a section that is moved, convert the section move to a delete+insert
+ const auto it = fromMap.find(move.from.section);
+ if (it != fromMap.end() && it->second != nil) {
+ IGListMoveIndex *sectionMove = it->second;
+ [mMoveIndexPaths removeObject:move];
+ [mMoveSections removeObject:sectionMove];
+ [mDeleteSections addIndex:sectionMove.from];
+ [mInsertSections addIndex:sectionMove.to];
+ }
+ }
+
+ _deleteSections = [mDeleteSections copy];
+ _insertSections = [mInsertSections copy];
+ _moveSections = [mMoveSections copy];
+ _deleteIndexPaths = [mDeleteIndexPaths copy];
+ _insertIndexPaths = [mInsertIndexPaths copy];
+ _updateIndexPaths = [updateIndexPaths copy];
+ _moveIndexPaths = [mMoveIndexPaths copy];
+ }
+ return self;
+}
+
+- (BOOL)isEqual:(id)object {
+ if (object == self) {
+ return YES;
+ }
+ if ([object isKindOfClass:[IGListBatchUpdateData class]]) {
+ return ([self.insertSections isEqual:[object insertSections]]
+ && [self.deleteSections isEqual:[object deleteSections]]
+ && [self.moveSections isEqual:[object moveSections]]
+ && [self.insertIndexPaths isEqual:[object insertIndexPaths]]
+ && [self.deleteIndexPaths isEqual:[object deleteIndexPaths]]
+ && [self.updateIndexPaths isEqual:[object updateIndexPaths]]
+ && [self.moveIndexPaths isEqual:[object moveIndexPaths]]);
+ }
+ return NO;
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@ %p; deleteSections: %lu; insertSections: %lu; moveSections: %lu; deleteIndexPaths: %lu; insertIndexPaths: %lu; updateIndexPaths: %lu>",
+ NSStringFromClass(self.class), self, (unsigned long)self.deleteSections.count, (unsigned long)self.insertSections.count, (unsigned long)self.moveSections.count,
+ (unsigned long)self.deleteIndexPaths.count, (unsigned long)self.insertIndexPaths.count, (unsigned long)self.updateIndexPaths.count];
+}
+
+@end
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListCompatibility.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListCompatibility.h
new file mode 100644
index 0000000..e9d9726
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListCompatibility.h
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#if TARGET_OS_EMBEDDED || TARGET_OS_SIMULATOR
+#import
+#else
+#import
+#endif
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiff.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiff.h
new file mode 100644
index 0000000..91fa26b
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiff.h
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#import
+#import
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ An option for how to do comparisons between similar objects.
+ */
+NS_SWIFT_NAME(ListDiffOption)
+typedef NS_ENUM(NSInteger, IGListDiffOption) {
+ /**
+ Compare objects using pointer personality.
+ */
+ IGListDiffPointerPersonality,
+ /**
+ Compare objects using `-[IGListDiffable isEqualToDiffableObject:]`.
+ */
+ IGListDiffEquality
+};
+
+/**
+ Creates a diff using indexes between two collections.
+
+ @param oldArray The old objects to diff against.
+ @param newArray The new objects.
+ @param option An option on how to compare objects.
+
+ @return A result object containing affected indexes.
+ */
+NS_SWIFT_NAME(ListDiff(oldArray:newArray:option:))
+FOUNDATION_EXTERN IGListIndexSetResult *IGListDiff(NSArray> *_Nullable oldArray,
+ NSArray> *_Nullable newArray,
+ IGListDiffOption option);
+
+/**
+ Creates a diff using index paths between two collections.
+
+ @param fromSection The old section.
+ @param toSection The new section.
+ @param oldArray The old objects to diff against.
+ @param newArray The new objects.
+ @param option An option on how to compare objects.
+
+ @return A result object containing affected indexes.
+ */
+NS_SWIFT_NAME(ListDiffPaths(fromSection:toSection:oldArray:newArray:option:))
+FOUNDATION_EXTERN IGListIndexPathResult *IGListDiffPaths(NSInteger fromSection,
+ NSInteger toSection,
+ NSArray> *_Nullable oldArray,
+ NSArray> *_Nullable newArray,
+ IGListDiffOption option);
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiff.mm b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiff.mm
new file mode 100644
index 0000000..12ccc02
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiff.mm
@@ -0,0 +1,348 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "IGListDiff.h"
+
+#import
+#import
+#import
+
+#import
+#import
+
+#import "IGListIndexPathResultInternal.h"
+#import "IGListIndexSetResultInternal.h"
+#import "IGListMoveIndexInternal.h"
+#import "IGListMoveIndexPathInternal.h"
+
+using namespace std;
+
+/// Used to track data stats while diffing.
+struct IGListEntry {
+ /// The number of times the data occurs in the old array
+ NSInteger oldCounter = 0;
+ /// The number of times the data occurs in the new array
+ NSInteger newCounter = 0;
+ /// The indexes of the data in the old array
+ stack oldIndexes;
+ /// Flag marking if the data has been updated between arrays by checking the isEqual: method
+ BOOL updated = NO;
+};
+
+/// Track both the entry and algorithm index. Default the index to NSNotFound
+struct IGListRecord {
+ IGListEntry *entry;
+ mutable NSInteger index;
+
+ IGListRecord() {
+ entry = NULL;
+ index = NSNotFound;
+ }
+};
+
+static id IGListTableKey(__unsafe_unretained id object) {
+ id key = [object diffIdentifier];
+ NSCAssert(key != nil, @"Cannot use a nil key for the diffIdentifier of object %@", object);
+ return key;
+}
+
+struct IGListEqualID {
+ bool operator()(const id a, const id b) const {
+ return (a == b) || [a isEqual: b];
+ }
+};
+
+struct IGListHashID {
+ size_t operator()(const id o) const {
+ return (size_t)[o hash];
+ }
+};
+
+static void addIndexToMap(BOOL useIndexPaths, NSInteger section, NSInteger index, __unsafe_unretained id object, __unsafe_unretained NSMapTable *map) {
+ id value;
+ if (useIndexPaths) {
+ value = [NSIndexPath indexPathForItem:index inSection:section];
+ } else {
+ value = @(index);
+ }
+ [map setObject:value forKey:[object diffIdentifier]];
+}
+
+static void addIndexToCollection(BOOL useIndexPaths, __unsafe_unretained id collection, NSInteger section, NSInteger index) {
+ if (useIndexPaths) {
+ NSIndexPath *path = [NSIndexPath indexPathForItem:index inSection:section];
+ [collection addObject:path];
+ } else {
+ [collection addIndex:index];
+ }
+};
+
+static NSArray *indexPathsAndPopulateMap(__unsafe_unretained NSArray> *array, NSInteger section, __unsafe_unretained NSMapTable *map) {
+ NSMutableArray *paths = [NSMutableArray new];
+ [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ NSIndexPath *path = [NSIndexPath indexPathForItem:idx inSection:section];
+ [paths addObject:path];
+ [map setObject:path forKey:[obj diffIdentifier]];
+ }];
+ return paths;
+}
+
+static id IGListDiffing(BOOL returnIndexPaths,
+ NSInteger fromSection,
+ NSInteger toSection,
+ NSArray> *oldArray,
+ NSArray> *newArray,
+ IGListDiffOption option,
+ IGListExperiment experiments) {
+ const NSInteger newCount = newArray.count;
+ const NSInteger oldCount = oldArray.count;
+
+ NSMapTable *oldMap = [NSMapTable strongToStrongObjectsMapTable];
+ NSMapTable *newMap = [NSMapTable strongToStrongObjectsMapTable];
+
+ // if no new objects, everything from the oldArray is deleted
+ // take a shortcut and just build a delete-everything result
+ if (newCount == 0) {
+ if (returnIndexPaths) {
+ return [[IGListIndexPathResult alloc] initWithInserts:[NSArray new]
+ deletes:indexPathsAndPopulateMap(oldArray, fromSection, oldMap)
+ updates:[NSArray new]
+ moves:[NSArray new]
+ oldIndexPathMap:oldMap
+ newIndexPathMap:newMap];
+ } else {
+ [oldArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ addIndexToMap(returnIndexPaths, fromSection, idx, obj, oldMap);
+ }];
+ return [[IGListIndexSetResult alloc] initWithInserts:[NSIndexSet new]
+ deletes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, oldCount)]
+ updates:[NSIndexSet new]
+ moves:[NSArray new]
+ oldIndexMap:oldMap
+ newIndexMap:newMap];
+ }
+ }
+
+ // if no old objects, everything from the newArray is inserted
+ // take a shortcut and just build an insert-everything result
+ if (oldCount == 0) {
+ if (returnIndexPaths) {
+ return [[IGListIndexPathResult alloc] initWithInserts:indexPathsAndPopulateMap(newArray, toSection, newMap)
+ deletes:[NSArray new]
+ updates:[NSArray new]
+ moves:[NSArray new]
+ oldIndexPathMap:oldMap
+ newIndexPathMap:newMap];
+ } else {
+ [newArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ addIndexToMap(returnIndexPaths, toSection, idx, obj, newMap);
+ }];
+ return [[IGListIndexSetResult alloc] initWithInserts:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newCount)]
+ deletes:[NSIndexSet new]
+ updates:[NSIndexSet new]
+ moves:[NSArray new]
+ oldIndexMap:oldMap
+ newIndexMap:newMap];
+ }
+ }
+
+ // symbol table uses the old/new array diffIdentifier as the key and IGListEntry as the value
+ // using id as the key provided by https://lists.gnu.org/archive/html/discuss-gnustep/2011-07/msg00019.html
+ unordered_map, IGListEntry, IGListHashID, IGListEqualID> table;
+
+ // pass 1
+ // create an entry for every item in the new array
+ // increment its new count for each occurence
+ vector newResultsArray(newCount);
+ for (NSInteger i = 0; i < newCount; i++) {
+ id key = IGListTableKey(newArray[i]);
+ IGListEntry &entry = table[key];
+ entry.newCounter++;
+
+ // add NSNotFound for each occurence of the item in the new array
+ entry.oldIndexes.push(NSNotFound);
+
+ // note: the entry is just a pointer to the entry which is stack-allocated in the table
+ newResultsArray[i].entry = &entry;
+ }
+
+ // pass 2
+ // update or create an entry for every item in the old array
+ // increment its old count for each occurence
+ // record the original index of the item in the old array
+ // MUST be done in descending order to respect the oldIndexes stack construction
+ vector oldResultsArray(oldCount);
+ for (NSInteger i = oldCount - 1; i >= 0; i--) {
+ id key = IGListTableKey(oldArray[i]);
+ IGListEntry &entry = table[key];
+ entry.oldCounter++;
+
+ // push the original indices where the item occurred onto the index stack
+ entry.oldIndexes.push(i);
+
+ // note: the entry is just a pointer to the entry which is stack-allocated in the table
+ oldResultsArray[i].entry = &entry;
+ }
+
+ // pass 3
+ // handle data that occurs in both arrays
+ for (NSInteger i = 0; i < newCount; i++) {
+ IGListEntry *entry = newResultsArray[i].entry;
+
+ // grab and pop the top original index. if the item was inserted this will be NSNotFound
+ NSCAssert(!entry->oldIndexes.empty(), @"Old indexes is empty while iterating new item %li. Should have NSNotFound", (long)i);
+ const NSInteger originalIndex = entry->oldIndexes.top();
+ entry->oldIndexes.pop();
+
+ if (originalIndex < oldCount) {
+ const id n = newArray[i];
+ const id o = oldArray[originalIndex];
+ switch (option) {
+ case IGListDiffPointerPersonality:
+ // flag the entry as updated if the pointers are not the same
+ if (n != o) {
+ entry->updated = YES;
+ }
+ break;
+ case IGListDiffEquality:
+ // use -[IGListDiffable isEqualToDiffableObject:] between both version of data to see if anything has changed
+ // skip the equality check if both indexes point to the same object
+ if (n != o && ![n isEqualToDiffableObject:o]) {
+ entry->updated = YES;
+ }
+ break;
+ }
+ }
+ if (originalIndex != NSNotFound
+ && entry->newCounter > 0
+ && entry->oldCounter > 0) {
+ // if an item occurs in the new and old array, it is unique
+ // assign the index of new and old records to the opposite index (reverse lookup)
+ newResultsArray[i].index = originalIndex;
+ oldResultsArray[originalIndex].index = i;
+ }
+ }
+
+ // storage for final NSIndexPaths or indexes
+ id mInserts, mMoves, mUpdates, mDeletes;
+ if (returnIndexPaths) {
+ mInserts = [NSMutableArray new];
+ mMoves = [NSMutableArray new];
+ mUpdates = [NSMutableArray new];
+ mDeletes = [NSMutableArray new];
+ } else {
+ mInserts = [NSMutableIndexSet new];
+ mMoves = [NSMutableArray new];
+ mUpdates = [NSMutableIndexSet new];
+ mDeletes = [NSMutableIndexSet new];
+ }
+
+ // track offsets from deleted items to calculate where items have moved
+ vector deleteOffsets(oldCount), insertOffsets(newCount);
+ NSInteger runningOffset = 0;
+
+ // iterate old array records checking for deletes
+ // incremement offset for each delete
+ for (NSInteger i = 0; i < oldCount; i++) {
+ deleteOffsets[i] = runningOffset;
+ const IGListRecord record = oldResultsArray[i];
+ // if the record index in the new array doesn't exist, its a delete
+ if (record.index == NSNotFound) {
+ addIndexToCollection(returnIndexPaths, mDeletes, fromSection, i);
+ runningOffset++;
+ }
+
+ addIndexToMap(returnIndexPaths, fromSection, i, oldArray[i], oldMap);
+ }
+
+ // reset and track offsets from inserted items to calculate where items have moved
+ runningOffset = 0;
+
+ for (NSInteger i = 0; i < newCount; i++) {
+ insertOffsets[i] = runningOffset;
+ const IGListRecord record = newResultsArray[i];
+ const NSInteger oldIndex = record.index;
+ // add to inserts if the opposing index is NSNotFound
+ if (record.index == NSNotFound) {
+ addIndexToCollection(returnIndexPaths, mInserts, toSection, i);
+ runningOffset++;
+ } else {
+ // note that an entry can be updated /and/ moved
+ if (record.entry->updated) {
+ addIndexToCollection(returnIndexPaths, mUpdates, fromSection, oldIndex);
+ }
+
+ // calculate the offset and determine if there was a move
+ // if the indexes match, ignore the index
+ const NSInteger insertOffset = insertOffsets[i];
+ const NSInteger deleteOffset = deleteOffsets[oldIndex];
+ if ((oldIndex - deleteOffset + insertOffset) != i) {
+ id move;
+ if (returnIndexPaths) {
+ NSIndexPath *from = [NSIndexPath indexPathForItem:oldIndex inSection:fromSection];
+ NSIndexPath *to = [NSIndexPath indexPathForItem:i inSection:toSection];
+ move = [[IGListMoveIndexPath alloc] initWithFrom:from to:to];
+ } else {
+ move = [[IGListMoveIndex alloc] initWithFrom:oldIndex to:i];
+ }
+ [mMoves addObject:move];
+ }
+ }
+
+ addIndexToMap(returnIndexPaths, toSection, i, newArray[i], newMap);
+ }
+
+ NSCAssert((oldCount + [mInserts count] - [mDeletes count]) == newCount,
+ @"Sanity check failed applying %lu inserts and %lu deletes to old count %li equaling new count %li",
+ (unsigned long)[mInserts count], (unsigned long)[mDeletes count], (long)oldCount, (long)newCount);
+
+ if (returnIndexPaths) {
+ return [[IGListIndexPathResult alloc] initWithInserts:mInserts
+ deletes:mDeletes
+ updates:mUpdates
+ moves:mMoves
+ oldIndexPathMap:oldMap
+ newIndexPathMap:newMap];
+ } else {
+ return [[IGListIndexSetResult alloc] initWithInserts:mInserts
+ deletes:mDeletes
+ updates:mUpdates
+ moves:mMoves
+ oldIndexMap:oldMap
+ newIndexMap:newMap];
+ }
+}
+
+IGListIndexSetResult *IGListDiff(NSArray > *oldArray,
+ NSArray> *newArray,
+ IGListDiffOption option) {
+ return IGListDiffing(NO, 0, 0, oldArray, newArray, option, 0);
+}
+
+IGListIndexPathResult *IGListDiffPaths(NSInteger fromSection,
+ NSInteger toSection,
+ NSArray> *oldArray,
+ NSArray> *newArray,
+ IGListDiffOption option) {
+ return IGListDiffing(YES, fromSection, toSection, oldArray, newArray, option, 0);
+}
+
+IGListIndexSetResult *IGListDiffExperiment(NSArray> *_Nullable oldArray,
+ NSArray> *_Nullable newArray,
+ IGListDiffOption option,
+ IGListExperiment experiments) {
+ return IGListDiffing(NO, 0, 0, oldArray, newArray, option, experiments);
+}
+
+IGListIndexPathResult *IGListDiffPathsExperiment(NSInteger fromSection,
+ NSInteger toSection,
+ NSArray> *_Nullable oldArray,
+ NSArray> *_Nullable newArray,
+ IGListDiffOption option,
+ IGListExperiment experiments) {
+ return IGListDiffing(YES, fromSection, toSection, oldArray, newArray, option, experiments);
+}
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiffKit.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiffKit.h
new file mode 100644
index 0000000..d889ba3
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiffKit.h
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+/**
+ * Project version number for IGListKit.
+ */
+FOUNDATION_EXPORT double IGListKitVersionNumber;
+
+/**
+ * Project version string for IGListKit.
+ */
+FOUNDATION_EXPORT const unsigned char IGListKitVersionString[];
+
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiffable.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiffable.h
new file mode 100644
index 0000000..5875daf
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListDiffable.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+/**
+ The `IGListDiffable` protocol provides methods needed to compare the identity and equality of two objects.
+ */
+NS_SWIFT_NAME(ListDiffable)
+@protocol IGListDiffable
+
+/**
+ Returns a key that uniquely identifies the object.
+
+ @return A key that can be used to uniquely identify the object.
+
+ @note Two objects may share the same identifier, but are not equal. A common pattern is to use the `NSObject`
+ category for automatic conformance. However this means that objects will be identified on their
+ pointer value so finding updates becomes impossible.
+
+ @warning This value should never be mutated.
+ */
+- (nonnull id)diffIdentifier;
+
+/**
+ Returns whether the receiver and a given object are equal.
+
+ @param object The object to be compared to the receiver.
+
+ @return `YES` if the receiver and object are equal, otherwise `NO`.
+ */
+- (BOOL)isEqualToDiffableObject:(nullable id)object;
+
+@end
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListExperiments.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListExperiments.h
new file mode 100644
index 0000000..009f3df
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListExperiments.h
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#import
+
+/**
+ Bitmask-able options used for pre-release feature testing.
+ */
+NS_SWIFT_NAME(ListExperiment)
+typedef NS_OPTIONS (NSInteger, IGListExperiment) {
+ /// Specifies no experiments.
+ IGListExperimentNone = 1 << 1,
+ /// Test updater diffing performed on a background queue.
+ IGListExperimentBackgroundDiffing = 1 << 2,
+ /// Test fallback to reloadData when "too many" update operations.
+ IGListExperimentReloadDataFallback = 1 << 3,
+ /// Test removing the layout pass when calling scrollToObject to avoid creating off-screen cells.
+ IGListExperimentAvoidLayoutOnScrollToObject = 1 << 4,
+ /// Test fixing a crash when inserting and deleting the same NSIndexPath multiple times.
+ IGListExperimentFixIndexPathImbalance = 1 << 5,
+ /// Test deferring object creation until just before diffing.
+ IGListExperimentDeferredToObjectCreation = 1 << 6,
+ /// Test getting collection view at update time.
+ IGListExperimentGetCollectionViewAtUpdate = 1 << 7,
+ /// Test invalidating layout when cell reloads/updates in IGListBindingSectionController.
+ IGListExperimentInvalidateLayoutForUpdates = 1 << 8,
+ /// Test using the collection view when asking for layout instead of accessing the data source. Only apply to IGListCollectionViewLayout.
+ IGListExperimentUseCollectionViewInsteadOfDataSourceInLayout = 1 << 9
+};
+
+/**
+ Check if an experiment is enabled in a bitmask.
+
+ @param mask The bitmask of experiments.
+ @param option The option to compare with.
+
+ @return `YES` if the option is in the bitmask, otherwise `NO`.
+ */
+NS_SWIFT_NAME(ListExperimentEnabled(mask:option:))
+static inline BOOL IGListExperimentEnabled(IGListExperiment mask, IGListExperiment option) {
+ return (mask & option) != 0;
+}
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Performs an index diff with an experiment bitmask.
+
+ @param oldArray The old array of objects.
+ @param newArray The new array of objects.
+ @param option Option to specify the type of diff.
+ @param experiments Optional experiments.
+
+ @return An index set result object contained the changed indexes.
+
+ @see `IGListDiff()`.
+ */
+NS_SWIFT_NAME(ListDiffExperiment(oldArray:newArray:option:experiments:))
+FOUNDATION_EXTERN IGListIndexSetResult *IGListDiffExperiment(NSArray> *_Nullable oldArray,
+ NSArray> *_Nullable newArray,
+ IGListDiffOption option,
+ IGListExperiment experiments);
+
+/**
+ Performs a index path diff with an experiment bitmask.
+
+ @param fromSection The old section.
+ @param toSection The new section.
+ @param oldArray The old array of objects.
+ @param newArray The new array of objects.
+ @param option Option to specify the type of diff.
+ @param experiments Optional experiments.
+
+ @return An index path result object containing the changed indexPaths.
+
+ @see `IGListDiffPaths()`.
+ */
+NS_SWIFT_NAME(ListDiffPathsExperiment(fromSection:toSection:oldArray:newArray:option:experiments:))
+FOUNDATION_EXTERN IGListIndexPathResult *IGListDiffPathsExperiment(NSInteger fromSection,
+ NSInteger toSection,
+ NSArray> *_Nullable oldArray,
+ NSArray> *_Nullable newArray,
+ IGListDiffOption option,
+ IGListExperiment experiments);
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexPathResult.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexPathResult.h
new file mode 100644
index 0000000..d0baedb
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexPathResult.h
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A result object returned when diffing with sections.
+ */
+NS_SWIFT_NAME(ListIndexPathResult)
+@interface IGListIndexPathResult : NSObject
+
+/**
+ The index paths inserted into the new collection.
+ */
+@property (nonatomic, copy, readonly) NSArray *inserts;
+
+/**
+ The index paths deleted from the old collection.
+ */
+@property (nonatomic, copy, readonly) NSArray *deletes;
+
+/**
+ The index paths in the old collection that need updated.
+ */
+@property (nonatomic, copy, readonly) NSArray *updates;
+
+/**
+ The moves from an index path in the old collection to an index path in the new collection.
+ */
+@property (nonatomic, copy, readonly) NSArray *moves;
+
+/**
+ A Read-only boolean that indicates whether the result has any changes or not.
+ `YES` if the result has changes, `NO` otherwise.
+ */
+@property (nonatomic, assign, readonly) BOOL hasChanges;
+
+/**
+ Returns the index path of the object with the specified identifier *before* the diff.
+
+ @param identifier The diff identifier of the object.
+
+ @return The index path of the object before the diff, or `nil`.
+
+ @see `-[IGListDiffable diffIdentifier]`.
+ */
+- (nullable NSIndexPath *)oldIndexPathForIdentifier:(id)identifier;
+
+/**
+ Returns the index path of the object with the specified identifier *after* the diff.
+
+ @param identifier The diff identifier of the object.
+
+ @return The index path of the object after the diff, or `nil`.
+
+ @see `-[IGListDiffable diffIdentifier]`.
+ */
+- (nullable NSIndexPath *)newIndexPathForIdentifier:(id)identifier;
+
+/**
+ Creates a new result object with operations safe for use in `UITableView` and `UICollectionView` batch updates.
+ */
+- (IGListIndexPathResult *)resultForBatchUpdates;
+
+/**
+ :nodoc:
+ */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ :nodoc:
+ */
++ (instancetype)new NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexPathResult.m b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexPathResult.m
new file mode 100644
index 0000000..46450d1
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexPathResult.m
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "IGListIndexPathResult.h"
+#import "IGListIndexPathResultInternal.h"
+
+@implementation IGListIndexPathResult {
+ NSMapTable, NSIndexPath *> *_oldIndexPathMap;
+ NSMapTable, NSIndexPath *> *_newIndexPathMap;
+}
+
+- (instancetype)initWithInserts:(NSArray *)inserts
+ deletes:(NSArray *)deletes
+ updates:(NSArray *)updates
+ moves:(NSArray *)moves
+ oldIndexPathMap:(NSMapTable, NSIndexPath *> *)oldIndexPathMap
+ newIndexPathMap:(NSMapTable, NSIndexPath *> *)newIndexPathMap {
+ if (self = [super init]) {
+ _inserts = inserts;
+ _deletes = deletes;
+ _updates = updates;
+ _moves = moves;
+ _oldIndexPathMap = oldIndexPathMap;
+ _newIndexPathMap = newIndexPathMap;
+ }
+ return self;
+}
+
+- (BOOL)hasChanges {
+ return self.changeCount > 0;
+}
+
+- (NSInteger)changeCount {
+ return self.inserts.count + self.deletes.count + self.updates.count + self.moves.count;
+}
+
+- (IGListIndexPathResult *)resultForBatchUpdates {
+ NSMutableSet *deletes = [NSMutableSet setWithArray:self.deletes];
+ NSMutableSet *inserts = [NSMutableSet setWithArray:self.inserts];
+ NSMutableSet *filteredUpdates = [NSMutableSet setWithArray:self.updates];
+
+ NSArray *moves = self.moves;
+ NSMutableArray *filteredMoves = [moves mutableCopy];
+
+ // convert move+update to delete+insert, respecting the from/to of the move
+ const NSInteger moveCount = moves.count;
+ for (NSInteger i = moveCount - 1; i >= 0; i--) {
+ IGListMoveIndexPath *move = moves[i];
+ if ([filteredUpdates containsObject:move.from]) {
+ [filteredMoves removeObjectAtIndex:i];
+ [filteredUpdates removeObject:move.from];
+ [deletes addObject:move.from];
+ [inserts addObject:move.to];
+ }
+ }
+
+ // iterate all new identifiers. if its index is updated, delete from the old index and insert the new index
+ for (id key in [_oldIndexPathMap keyEnumerator]) {
+ NSIndexPath *indexPath = [_oldIndexPathMap objectForKey:key];
+ if ([filteredUpdates containsObject:indexPath]) {
+ [deletes addObject:indexPath];
+ [inserts addObject:(id)[_newIndexPathMap objectForKey:key]];
+ }
+ }
+
+ return [[IGListIndexPathResult alloc] initWithInserts:[inserts allObjects]
+ deletes:[deletes allObjects]
+ updates:[NSArray new]
+ moves:filteredMoves
+ oldIndexPathMap:_oldIndexPathMap
+ newIndexPathMap:_newIndexPathMap];
+}
+
+- (NSIndexPath *)oldIndexPathForIdentifier:(id)identifier {
+ return [_oldIndexPathMap objectForKey:identifier];
+}
+
+- (NSIndexPath *)newIndexPathForIdentifier:(id)identifier {
+ return [_newIndexPathMap objectForKey:identifier];
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@ %p; %lu inserts; %lu deletes; %lu updates; %lu moves>",
+ NSStringFromClass(self.class), self, (unsigned long)self.inserts.count, (unsigned long)self.deletes.count, (unsigned long)self.updates.count, (unsigned long)self.moves.count];
+}
+
+@end
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexSetResult.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexSetResult.h
new file mode 100644
index 0000000..6c81d5f
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexSetResult.h
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A result object returned when diffing with indexes.
+ */
+NS_SWIFT_NAME(ListIndexSetResult)
+@interface IGListIndexSetResult : NSObject
+
+/**
+ The indexes inserted into the new collection.
+ */
+@property (nonatomic, strong, readonly) NSIndexSet *inserts;
+
+/**
+ The indexes deleted from the old collection.
+ */
+@property (nonatomic, strong, readonly) NSIndexSet *deletes;
+
+/**
+ The indexes in the old collection that need updated.
+ */
+@property (nonatomic, strong, readonly) NSIndexSet *updates;
+
+/**
+ The moves from an index in the old collection to an index in the new collection.
+ */
+@property (nonatomic, copy, readonly) NSArray *moves;
+
+/**
+ A Read-only boolean that indicates whether the result has any changes or not.
+ `YES` if the result has changes, `NO` otherwise.
+ */
+@property (nonatomic, assign, readonly) BOOL hasChanges;
+
+/**
+ Returns the index of the object with the specified identifier *before* the diff.
+
+ @param identifier The diff identifier of the object.
+
+ @return The index of the object before the diff, or `NSNotFound`.
+
+ @see `-[IGListDiffable diffIdentifier]`.
+ */
+- (NSInteger)oldIndexForIdentifier:(id)identifier;
+
+/**
+ Returns the index of the object with the specified identifier *after* the diff.
+
+ @param identifier The diff identifier of the object.
+
+ @return The index path of the object after the diff, or `NSNotFound`.
+
+ @see `-[IGListDiffable diffIdentifier]`.
+ */
+- (NSInteger)newIndexForIdentifier:(id)identifier;
+
+/**
+ Creates a new result object with operations safe for use in `UITableView` and `UICollectionView` batch updates.
+ */
+- (IGListIndexSetResult *)resultForBatchUpdates;
+
+/**
+ :nodoc:
+ */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ :nodoc:
+ */
++ (instancetype)new NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexSetResult.m b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexSetResult.m
new file mode 100644
index 0000000..74853e5
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListIndexSetResult.m
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "IGListIndexSetResult.h"
+#import "IGListIndexSetResultInternal.h"
+
+@implementation IGListIndexSetResult {
+ NSMapTable, NSNumber *> *_oldIndexMap;
+ NSMapTable, NSNumber *> *_newIndexMap;
+}
+
+- (instancetype)initWithInserts:(NSIndexSet *)inserts
+ deletes:(NSIndexSet *)deletes
+ updates:(NSIndexSet *)updates
+ moves:(NSArray *)moves
+ oldIndexMap:(NSMapTable, NSNumber *> *)oldIndexMap
+ newIndexMap:(NSMapTable, NSNumber *> *)newIndexMap {
+ if (self = [super init]) {
+ _inserts = inserts;
+ _deletes = deletes;
+ _updates = updates;
+ _moves = moves;
+ _oldIndexMap = oldIndexMap;
+ _newIndexMap = newIndexMap;
+ }
+ return self;
+}
+
+- (BOOL)hasChanges {
+ return self.changeCount > 0;
+}
+
+- (NSInteger)changeCount {
+ return self.inserts.count + self.deletes.count + self.updates.count + self.moves.count;
+}
+
+- (IGListIndexSetResult *)resultForBatchUpdates {
+ NSMutableIndexSet *deletes = [self.deletes mutableCopy];
+ NSMutableIndexSet *inserts = [self.inserts mutableCopy];
+ NSMutableIndexSet *filteredUpdates = [self.updates mutableCopy];
+
+ NSArray *moves = self.moves;
+ NSMutableArray *filteredMoves = [moves mutableCopy];
+
+ // convert all update+move to delete+insert
+ const NSInteger moveCount = moves.count;
+ for (NSInteger i = moveCount - 1; i >= 0; i--) {
+ IGListMoveIndex *move = moves[i];
+ if ([filteredUpdates containsIndex:move.from]) {
+ [filteredMoves removeObjectAtIndex:i];
+ [filteredUpdates removeIndex:move.from];
+ [deletes addIndex:move.from];
+ [inserts addIndex:move.to];
+ }
+ }
+
+ // iterate all new identifiers. if its index is updated, delete from the old index and insert the new index
+ for (id key in [_oldIndexMap keyEnumerator]) {
+ const NSInteger index = [[_oldIndexMap objectForKey:key] integerValue];
+ if ([filteredUpdates containsIndex:index]) {
+ [deletes addIndex:index];
+ [inserts addIndex:[[_newIndexMap objectForKey:key] integerValue]];
+ }
+ }
+
+ return [[IGListIndexSetResult alloc] initWithInserts:inserts
+ deletes:deletes
+ updates:[NSIndexSet new]
+ moves:filteredMoves
+ oldIndexMap:_oldIndexMap
+ newIndexMap:_newIndexMap];
+}
+
+- (NSInteger)oldIndexForIdentifier:(id)identifier {
+ NSNumber *index = [_oldIndexMap objectForKey:identifier];
+ return index == nil ? NSNotFound : [index integerValue];
+}
+
+- (NSInteger)newIndexForIdentifier:(id)identifier {
+ NSNumber *index = [_newIndexMap objectForKey:identifier];
+ return index == nil ? NSNotFound : [index integerValue];
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@ %p; %lu inserts; %lu deletes; %lu updates; %lu moves>",
+ NSStringFromClass(self.class), self, (unsigned long)self.inserts.count, (unsigned long)self.deletes.count, (unsigned long)self.updates.count, (unsigned long)self.moves.count];
+}
+
+@end
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMacros.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMacros.h
new file mode 100644
index 0000000..5b5ef87
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMacros.h
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#ifndef IGLK_SUBCLASSING_RESTRICTED
+#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
+#define IGLK_SUBCLASSING_RESTRICTED __attribute__((objc_subclassing_restricted))
+#else
+#define IGLK_SUBCLASSING_RESTRICTED
+#endif // #if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
+#endif // #ifndef IGLK_SUBCLASSING_RESTRICTED
+
+#ifndef IGLK_UNAVAILABLE
+#define IGLK_UNAVAILABLE(message) __attribute__((unavailable(message)))
+#endif // #ifndef IGLK_UNAVAILABLE
+
+#if IGLK_LOGGING_ENABLED
+#define IGLKLog( s, ... ) do { NSLog( @"IGListKit: %@", [NSString stringWithFormat: (s), ##__VA_ARGS__] ); } while(0)
+#else
+#define IGLKLog( s, ... )
+#endif
+
+#ifndef IGLK_DEBUG_DESCRIPTION_ENABLED
+#define IGLK_DEBUG_DESCRIPTION_ENABLED DEBUG
+#endif // #ifndef IGLK_DEBUG_DESCRIPTION_ENABLED
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndex.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndex.h
new file mode 100644
index 0000000..1849bb8
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndex.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ An object representing a move between indexes.
+ */
+NS_SWIFT_NAME(ListMoveIndex)
+@interface IGListMoveIndex : NSObject
+
+/**
+ An index in the old collection.
+ */
+@property (nonatomic, assign, readonly) NSInteger from;
+
+/**
+ An index in the new collection.
+ */
+@property (nonatomic, assign, readonly) NSInteger to;
+
+/**
+ :nodoc:
+ */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ :nodoc:
+ */
++ (instancetype)new NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndex.m b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndex.m
new file mode 100644
index 0000000..dee66f4
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndex.m
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "IGListMoveIndex.h"
+
+@implementation IGListMoveIndex
+
+- (instancetype)initWithFrom:(NSInteger)from to:(NSInteger)to {
+ if (self = [super init]) {
+ _from = from;
+ _to = to;
+ }
+ return self;
+}
+
+- (NSUInteger)hash {
+ return _from ^ _to;
+}
+
+- (BOOL)isEqual:(id)object {
+ if (object == self) {
+ return YES;
+ }
+ if ([object isKindOfClass:[IGListMoveIndex class]]) {
+ const NSInteger f1 = self.from, f2 = [object from];
+ const NSInteger t1 = self.to, t2 = [object to];
+ return f1 == f2 && t1 == t2;
+ }
+ return NO;
+}
+
+- (NSComparisonResult)compare:(id)object {
+ const NSInteger right = [object from];
+ const NSInteger left = [self from];
+ if (left == right) {
+ return NSOrderedSame;
+ } else if (left < right) {
+ return NSOrderedAscending;
+ } else {
+ return NSOrderedDescending;
+ }
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@ %p; from: %li; to: %li;>", NSStringFromClass(self.class), self, (long)self.from, (long)self.to];
+}
+
+@end
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndexPath.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndexPath.h
new file mode 100644
index 0000000..30b7070
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndexPath.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ An object representing a move between indexes.
+ */
+NS_SWIFT_NAME(ListMoveIndexPath)
+@interface IGListMoveIndexPath : NSObject
+
+/**
+ An index path in the old collection.
+ */
+@property (nonatomic, strong, readonly) NSIndexPath *from;
+
+/**
+ An index path in the new collection.
+ */
+@property (nonatomic, strong, readonly) NSIndexPath *to;
+
+/**
+ :nodoc:
+ */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ :nodoc:
+ */
++ (instancetype)new NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndexPath.m b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndexPath.m
new file mode 100644
index 0000000..7517794
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/IGListMoveIndexPath.m
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "IGListMoveIndexPath.h"
+
+@implementation IGListMoveIndexPath
+
+- (instancetype)initWithFrom:(NSIndexPath *)from to:(NSIndexPath *)to {
+ NSParameterAssert(from != nil);
+ NSParameterAssert(to != nil);
+ if (self = [super init]) {
+ _from = from;
+ _to = to;
+ }
+ return self;
+}
+
+- (NSUInteger)hash {
+ return [_from hash] ^ [_to hash];
+}
+
+- (BOOL)isEqual:(id)object {
+ if (object == self) {
+ return YES;
+ }
+ if ([object isKindOfClass:[IGListMoveIndexPath class]]) {
+ NSIndexPath *f1 = self.from, *f2 = [object from];
+ NSIndexPath *t1 = self.to, *t2 = [object to];
+ return [f1 isEqual:f2] && [t1 isEqual:t2];
+ }
+ return NO;
+}
+
+- (NSComparisonResult)compare:(id)object {
+ return [[self from] compare:[object from]];
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@ %p; from: %@; to: %@;>", NSStringFromClass(self.class), self, self.from, self.to];
+}
+
+@end
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/Internal/IGListIndexPathResultInternal.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/Internal/IGListIndexPathResultInternal.h
new file mode 100644
index 0000000..8fec459
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/Internal/IGListIndexPathResultInternal.h
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface IGListIndexPathResult()
+
+- (instancetype)initWithInserts:(NSArray *)inserts
+ deletes:(NSArray *)deletes
+ updates:(NSArray *)updates
+ moves:(NSArray *)moves
+ oldIndexPathMap:(NSMapTable, NSIndexPath *> *)oldIndexPathMap
+ newIndexPathMap:(NSMapTable, NSIndexPath *> *)newIndexPathMap;
+
+@property (nonatomic, assign, readonly) NSInteger changeCount;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/Internal/IGListIndexSetResultInternal.h b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/Internal/IGListIndexSetResultInternal.h
new file mode 100644
index 0000000..db7f14a
--- /dev/null
+++ b/Demo/Pods/IGListDiffKit/Source/IGListDiffKit/Internal/IGListIndexSetResultInternal.h
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+#import