Repository: sessionCh/RxXMLY Branch: master Commit: 20c42183e93e Files: 3400 Total size: 98.0 MB Directory structure: gitextract_mpyk4p85/ ├── .gitigonre ├── Podfile ├── Pods/ │ ├── Alamofire/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Source/ │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── MultipartFormData.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── SessionDelegate.swift │ │ ├── SessionManager.swift │ │ ├── TaskDelegate.swift │ │ ├── Timeline.swift │ │ └── Validation.swift │ ├── Differentiator/ │ │ ├── LICENSE.md │ │ ├── README.md │ │ └── Sources/ │ │ └── Differentiator/ │ │ ├── AnimatableSectionModel.swift │ │ ├── AnimatableSectionModelType+ItemPath.swift │ │ ├── AnimatableSectionModelType.swift │ │ ├── Changeset.swift │ │ ├── Diff.swift │ │ ├── IdentifiableType.swift │ │ ├── IdentifiableValue.swift │ │ ├── ItemPath.swift │ │ ├── Optional+Extensions.swift │ │ ├── SectionModel.swift │ │ ├── SectionModelType.swift │ │ └── Utilities.swift │ ├── IQKeyboardManagerSwift/ │ │ ├── IQKeyboardManagerSwift/ │ │ │ ├── Categories/ │ │ │ │ ├── IQNSArray+Sort.swift │ │ │ │ ├── IQUIScrollView+Additions.swift │ │ │ │ ├── IQUITextFieldView+Additions.swift │ │ │ │ ├── IQUIView+Hierarchy.swift │ │ │ │ ├── IQUIViewController+Additions.swift │ │ │ │ └── IQUIWindow+Hierarchy.swift │ │ │ ├── Constants/ │ │ │ │ ├── IQKeyboardManagerConstants.swift │ │ │ │ └── IQKeyboardManagerConstantsInternal.swift │ │ │ ├── IQKeyboardManager.swift │ │ │ ├── IQKeyboardReturnKeyHandler.swift │ │ │ ├── IQTextView/ │ │ │ │ └── IQTextView.swift │ │ │ └── IQToolbar/ │ │ │ ├── IQBarButtonItem.swift │ │ │ ├── IQPreviousNextView.swift │ │ │ ├── IQTitleBarButtonItem.swift │ │ │ ├── IQToolbar.swift │ │ │ └── IQUIView+IQKeyboardToolbar.swift │ │ ├── LICENSE.md │ │ └── README.md │ ├── Kingfisher/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources/ │ │ ├── AnimatedImageView.swift │ │ ├── Box.swift │ │ ├── CacheSerializer.swift │ │ ├── Filter.swift │ │ ├── FormatIndicatedCacheSerializer.swift │ │ ├── Image.swift │ │ ├── ImageCache.swift │ │ ├── ImageDownloader.swift │ │ ├── ImagePrefetcher.swift │ │ ├── ImageProcessor.swift │ │ ├── ImageTransition.swift │ │ ├── ImageView+Kingfisher.swift │ │ ├── Indicator.swift │ │ ├── Kingfisher.h │ │ ├── Kingfisher.swift │ │ ├── KingfisherManager.swift │ │ ├── KingfisherOptionsInfo.swift │ │ ├── Placeholder.swift │ │ ├── RequestModifier.swift │ │ ├── Resource.swift │ │ ├── String+MD5.swift │ │ ├── ThreadHelper.swift │ │ └── UIButton+Kingfisher.swift │ ├── MJRefresh/ │ │ ├── LICENSE │ │ ├── MJRefresh/ │ │ │ ├── Base/ │ │ │ │ ├── MJRefreshAutoFooter.h │ │ │ │ ├── MJRefreshAutoFooter.m │ │ │ │ ├── MJRefreshBackFooter.h │ │ │ │ ├── MJRefreshBackFooter.m │ │ │ │ ├── MJRefreshComponent.h │ │ │ │ ├── MJRefreshComponent.m │ │ │ │ ├── MJRefreshFooter.h │ │ │ │ ├── MJRefreshFooter.m │ │ │ │ ├── MJRefreshHeader.h │ │ │ │ └── MJRefreshHeader.m │ │ │ ├── Custom/ │ │ │ │ ├── Footer/ │ │ │ │ │ ├── Auto/ │ │ │ │ │ │ ├── MJRefreshAutoGifFooter.h │ │ │ │ │ │ ├── MJRefreshAutoGifFooter.m │ │ │ │ │ │ ├── MJRefreshAutoNormalFooter.h │ │ │ │ │ │ ├── MJRefreshAutoNormalFooter.m │ │ │ │ │ │ ├── MJRefreshAutoStateFooter.h │ │ │ │ │ │ └── MJRefreshAutoStateFooter.m │ │ │ │ │ └── Back/ │ │ │ │ │ ├── MJRefreshBackGifFooter.h │ │ │ │ │ ├── MJRefreshBackGifFooter.m │ │ │ │ │ ├── MJRefreshBackNormalFooter.h │ │ │ │ │ ├── MJRefreshBackNormalFooter.m │ │ │ │ │ ├── MJRefreshBackStateFooter.h │ │ │ │ │ └── MJRefreshBackStateFooter.m │ │ │ │ └── Header/ │ │ │ │ ├── MJRefreshGifHeader.h │ │ │ │ ├── MJRefreshGifHeader.m │ │ │ │ ├── MJRefreshNormalHeader.h │ │ │ │ ├── MJRefreshNormalHeader.m │ │ │ │ ├── MJRefreshStateHeader.h │ │ │ │ └── MJRefreshStateHeader.m │ │ │ ├── MJRefresh.bundle/ │ │ │ │ ├── en.lproj/ │ │ │ │ │ └── Localizable.strings │ │ │ │ ├── zh-Hans.lproj/ │ │ │ │ │ └── Localizable.strings │ │ │ │ └── zh-Hant.lproj/ │ │ │ │ └── Localizable.strings │ │ │ ├── MJRefresh.h │ │ │ ├── MJRefreshConst.h │ │ │ ├── MJRefreshConst.m │ │ │ ├── NSBundle+MJRefresh.h │ │ │ ├── NSBundle+MJRefresh.m │ │ │ ├── UIScrollView+MJExtension.h │ │ │ ├── UIScrollView+MJExtension.m │ │ │ ├── UIScrollView+MJRefresh.h │ │ │ ├── UIScrollView+MJRefresh.m │ │ │ ├── UIView+MJExtension.h │ │ │ └── UIView+MJExtension.m │ │ └── README.md │ ├── Moya/ │ │ ├── License.md │ │ ├── Readme.md │ │ └── Sources/ │ │ └── Moya/ │ │ ├── AnyEncodable.swift │ │ ├── Cancellable.swift │ │ ├── Endpoint.swift │ │ ├── Image.swift │ │ ├── Moya+Alamofire.swift │ │ ├── MoyaError.swift │ │ ├── MoyaProvider+Defaults.swift │ │ ├── MoyaProvider+Internal.swift │ │ ├── MoyaProvider.swift │ │ ├── MultiTarget.swift │ │ ├── MultipartFormData.swift │ │ ├── Plugin.swift │ │ ├── Plugins/ │ │ │ ├── AccessTokenPlugin.swift │ │ │ ├── CredentialsPlugin.swift │ │ │ ├── NetworkActivityPlugin.swift │ │ │ └── NetworkLoggerPlugin.swift │ │ ├── Response.swift │ │ ├── TargetType.swift │ │ ├── Task.swift │ │ ├── URL+Moya.swift │ │ └── URLRequest+Encoding.swift │ ├── NSObject+Rx/ │ │ ├── HasDisposeBag.swift │ │ ├── LICENSE │ │ ├── NSObject+Rx.swift │ │ └── Readme.md │ ├── ObjectMapper/ │ │ ├── LICENSE │ │ ├── README-CN.md │ │ └── Sources/ │ │ ├── CustomDateFormatTransform.swift │ │ ├── DataTransform.swift │ │ ├── DateFormatterTransform.swift │ │ ├── DateTransform.swift │ │ ├── DictionaryTransform.swift │ │ ├── EnumOperators.swift │ │ ├── EnumTransform.swift │ │ ├── FromJSON.swift │ │ ├── HexColorTransform.swift │ │ ├── ISO8601DateTransform.swift │ │ ├── ImmutableMappable.swift │ │ ├── IntegerOperators.swift │ │ ├── Map.swift │ │ ├── MapError.swift │ │ ├── Mappable.swift │ │ ├── Mapper.swift │ │ ├── NSDecimalNumberTransform.swift │ │ ├── Operators.swift │ │ ├── ToJSON.swift │ │ ├── TransformOf.swift │ │ ├── TransformOperators.swift │ │ ├── TransformType.swift │ │ └── URLTransform.swift │ ├── Pods.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcuserdata/ │ │ └── sessionCh.xcuserdatad/ │ │ └── xcschemes/ │ │ ├── Alamofire.xcscheme │ │ ├── Differentiator.xcscheme │ │ ├── IQKeyboardManagerSwift.xcscheme │ │ ├── Kingfisher.xcscheme │ │ ├── MJRefresh.xcscheme │ │ ├── Moya.xcscheme │ │ ├── NSObject+Rx.xcscheme │ │ ├── ObjectMapper.xcscheme │ │ ├── Pods-RxXMLY.xcscheme │ │ ├── ReactorKit.xcscheme │ │ ├── Result.xcscheme │ │ ├── ReusableKit.xcscheme │ │ ├── RxAlamofire.xcscheme │ │ ├── RxCocoa.xcscheme │ │ ├── RxDataSources.xcscheme │ │ ├── RxGesture.xcscheme │ │ ├── RxSwift.xcscheme │ │ ├── SnapKit.xcscheme │ │ ├── SwiftyColor.xcscheme │ │ ├── SwiftyJSON.xcscheme │ │ ├── TTRangeSlider.xcscheme │ │ ├── TYCyclePagerView.xcscheme │ │ ├── TYPagerController.xcscheme │ │ ├── Then.xcscheme │ │ ├── URLNavigator.xcscheme │ │ └── xcschememanagement.plist │ ├── ReactorKit/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources/ │ │ ├── ReactorKit/ │ │ │ ├── ActionSubject.swift │ │ │ ├── AssociatedObjectStore.swift │ │ │ ├── Reactor.swift │ │ │ ├── StoryboardView.swift │ │ │ ├── Stub.swift │ │ │ └── View.swift │ │ └── ReactorKitRuntime/ │ │ ├── ReactorKitRuntime.m │ │ └── include/ │ │ └── ReactorKitRuntime.h │ ├── Result/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Result/ │ │ ├── Result.swift │ │ └── ResultProtocol.swift │ ├── ReusableKit/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources/ │ │ ├── ReusableKit/ │ │ │ ├── ReusableKit.swift │ │ │ ├── UICollectionView+ReusableKit.swift │ │ │ └── UITableView+ReusableKit.swift │ │ └── RxReusableKit/ │ │ ├── UICollectionView+RxReusableKit.swift │ │ └── UITableView+RxReusableKit.swift │ ├── RxAlamofire/ │ │ ├── LICENSE.md │ │ ├── README.md │ │ └── Sources/ │ │ └── RxAlamofire.swift │ ├── RxCocoa/ │ │ ├── LICENSE.md │ │ ├── Platform/ │ │ │ ├── DataStructures/ │ │ │ │ ├── Bag.swift │ │ │ │ ├── InfiniteSequence.swift │ │ │ │ ├── PriorityQueue.swift │ │ │ │ └── Queue.swift │ │ │ ├── DispatchQueue+Extensions.swift │ │ │ ├── Platform.Darwin.swift │ │ │ ├── Platform.Linux.swift │ │ │ └── RecursiveLock.swift │ │ ├── README.md │ │ └── RxCocoa/ │ │ ├── Common/ │ │ │ ├── Binder.swift │ │ │ ├── ControlTarget.swift │ │ │ ├── DelegateProxy.swift │ │ │ ├── DelegateProxyType.swift │ │ │ ├── NSLayoutConstraint+Rx.swift │ │ │ ├── Observable+Bind.swift │ │ │ ├── RxCocoaObjCRuntimeError+Extensions.swift │ │ │ ├── RxTarget.swift │ │ │ ├── SectionedViewDataSourceType.swift │ │ │ └── TextInput.swift │ │ ├── Deprecated.swift │ │ ├── Foundation/ │ │ │ ├── KVORepresentable+CoreGraphics.swift │ │ │ ├── KVORepresentable+Swift.swift │ │ │ ├── KVORepresentable.swift │ │ │ ├── Logging.swift │ │ │ ├── NSObject+Rx+KVORepresentable.swift │ │ │ ├── NSObject+Rx+RawRepresentable.swift │ │ │ ├── NSObject+Rx.swift │ │ │ ├── NotificationCenter+Rx.swift │ │ │ └── URLSession+Rx.swift │ │ ├── Runtime/ │ │ │ ├── _RX.m │ │ │ ├── _RXDelegateProxy.m │ │ │ ├── _RXKVOObserver.m │ │ │ ├── _RXObjCRuntime.m │ │ │ └── include/ │ │ │ ├── RxCocoaRuntime.h │ │ │ ├── _RX.h │ │ │ ├── _RXDelegateProxy.h │ │ │ ├── _RXKVOObserver.h │ │ │ └── _RXObjCRuntime.h │ │ ├── RxCocoa.h │ │ ├── RxCocoa.swift │ │ ├── Traits/ │ │ │ ├── BehaviorRelay.swift │ │ │ ├── ControlEvent.swift │ │ │ ├── ControlProperty.swift │ │ │ ├── Driver/ │ │ │ │ ├── BehaviorRelay+Driver.swift │ │ │ │ ├── ControlEvent+Driver.swift │ │ │ │ ├── ControlProperty+Driver.swift │ │ │ │ ├── Driver+Subscription.swift │ │ │ │ ├── Driver.swift │ │ │ │ └── ObservableConvertibleType+Driver.swift │ │ │ ├── PublishRelay.swift │ │ │ ├── SharedSequence/ │ │ │ │ ├── SchedulerType+SharedSequence.swift │ │ │ │ ├── SharedSequence+Operators+arity.swift │ │ │ │ ├── SharedSequence+Operators.swift │ │ │ │ └── SharedSequence.swift │ │ │ └── Signal/ │ │ │ ├── ObservableConvertibleType+Signal.swift │ │ │ ├── PublishRelay+Signal.swift │ │ │ ├── Signal+Subscription.swift │ │ │ └── Signal.swift │ │ └── iOS/ │ │ ├── DataSources/ │ │ │ ├── RxCollectionViewReactiveArrayDataSource.swift │ │ │ ├── RxPickerViewAdapter.swift │ │ │ └── RxTableViewReactiveArrayDataSource.swift │ │ ├── Events/ │ │ │ └── ItemEvents.swift │ │ ├── NSTextStorage+Rx.swift │ │ ├── Protocols/ │ │ │ ├── RxCollectionViewDataSourceType.swift │ │ │ ├── RxPickerViewDataSourceType.swift │ │ │ └── RxTableViewDataSourceType.swift │ │ ├── Proxies/ │ │ │ ├── RxCollectionViewDataSourceProxy.swift │ │ │ ├── RxCollectionViewDelegateProxy.swift │ │ │ ├── RxNavigationControllerDelegateProxy.swift │ │ │ ├── RxPickerViewDataSourceProxy.swift │ │ │ ├── RxPickerViewDelegateProxy.swift │ │ │ ├── RxScrollViewDelegateProxy.swift │ │ │ ├── RxSearchBarDelegateProxy.swift │ │ │ ├── RxSearchControllerDelegateProxy.swift │ │ │ ├── RxTabBarControllerDelegateProxy.swift │ │ │ ├── RxTabBarDelegateProxy.swift │ │ │ ├── RxTableViewDataSourceProxy.swift │ │ │ ├── RxTableViewDelegateProxy.swift │ │ │ ├── RxTextStorageDelegateProxy.swift │ │ │ ├── RxTextViewDelegateProxy.swift │ │ │ └── RxWebViewDelegateProxy.swift │ │ ├── UIActivityIndicatorView+Rx.swift │ │ ├── UIAlertAction+Rx.swift │ │ ├── UIApplication+Rx.swift │ │ ├── UIBarButtonItem+Rx.swift │ │ ├── UIButton+Rx.swift │ │ ├── UICollectionView+Rx.swift │ │ ├── UIControl+Rx.swift │ │ ├── UIDatePicker+Rx.swift │ │ ├── UIGestureRecognizer+Rx.swift │ │ ├── UIImageView+Rx.swift │ │ ├── UILabel+Rx.swift │ │ ├── UINavigationController+Rx.swift │ │ ├── UINavigationItem+Rx.swift │ │ ├── UIPageControl+Rx.swift │ │ ├── UIPickerView+Rx.swift │ │ ├── UIProgressView+Rx.swift │ │ ├── UIRefreshControl+Rx.swift │ │ ├── UIScrollView+Rx.swift │ │ ├── UISearchBar+Rx.swift │ │ ├── UISearchController+Rx.swift │ │ ├── UISegmentedControl+Rx.swift │ │ ├── UISlider+Rx.swift │ │ ├── UIStepper+Rx.swift │ │ ├── UISwitch+Rx.swift │ │ ├── UITabBar+Rx.swift │ │ ├── UITabBarController+Rx.swift │ │ ├── UITabBarItem+Rx.swift │ │ ├── UITableView+Rx.swift │ │ ├── UITextField+Rx.swift │ │ ├── UITextView+Rx.swift │ │ ├── UIView+Rx.swift │ │ ├── UIViewController+Rx.swift │ │ └── UIWebView+Rx.swift │ ├── RxDataSources/ │ │ ├── LICENSE.md │ │ ├── README.md │ │ └── Sources/ │ │ └── RxDataSources/ │ │ ├── AnimationConfiguration.swift │ │ ├── Array+Extensions.swift │ │ ├── CollectionViewSectionedDataSource.swift │ │ ├── DataSources.swift │ │ ├── Deprecated.swift │ │ ├── FloatingPointType+IdentifiableType.swift │ │ ├── IntegerType+IdentifiableType.swift │ │ ├── RxCollectionViewSectionedAnimatedDataSource.swift │ │ ├── RxCollectionViewSectionedReloadDataSource.swift │ │ ├── RxPickerViewAdapter.swift │ │ ├── RxTableViewSectionedAnimatedDataSource.swift │ │ ├── RxTableViewSectionedReloadDataSource.swift │ │ ├── String+IdentifiableType.swift │ │ ├── TableViewSectionedDataSource.swift │ │ ├── UI+SectionedViewType.swift │ │ └── ViewTransition.swift │ ├── RxGesture/ │ │ ├── LICENSE │ │ ├── Pod/ │ │ │ └── Classes/ │ │ │ ├── GestureFactory.swift │ │ │ ├── RxGestureRecognizerDelegate.swift │ │ │ ├── SharedTypes.swift │ │ │ ├── View+RxGesture.swift │ │ │ └── iOS/ │ │ │ ├── TransformGestureRecognizers.swift │ │ │ ├── UIGestureRecognizer+RxGesture.swift │ │ │ ├── UILongPressGestureRecognizer+RxGesture.swift │ │ │ ├── UIPanGestureRecognizer+RxGesture.swift │ │ │ ├── UIPinchGestureRecognizer+RxGesture.swift │ │ │ ├── UIRotationGestureRecognizer+RxGesture.swift │ │ │ ├── UIScreenEdgePanGestureRecognizer+RxGesture.swift │ │ │ ├── UISwipeGestureRecognizer+RxGesture.swift │ │ │ └── UITapGestureRecognizer+RxGesture.swift │ │ └── README.md │ ├── RxSwift/ │ │ ├── LICENSE.md │ │ ├── Platform/ │ │ │ ├── DataStructures/ │ │ │ │ ├── Bag.swift │ │ │ │ ├── InfiniteSequence.swift │ │ │ │ ├── PriorityQueue.swift │ │ │ │ └── Queue.swift │ │ │ ├── DispatchQueue+Extensions.swift │ │ │ ├── Platform.Darwin.swift │ │ │ ├── Platform.Linux.swift │ │ │ └── RecursiveLock.swift │ │ ├── README.md │ │ └── RxSwift/ │ │ ├── AnyObserver.swift │ │ ├── Cancelable.swift │ │ ├── Concurrency/ │ │ │ ├── AsyncLock.swift │ │ │ ├── Lock.swift │ │ │ ├── LockOwnerType.swift │ │ │ ├── SynchronizedDisposeType.swift │ │ │ ├── SynchronizedOnType.swift │ │ │ └── SynchronizedUnsubscribeType.swift │ │ ├── ConnectableObservableType.swift │ │ ├── Deprecated.swift │ │ ├── Disposable.swift │ │ ├── Disposables/ │ │ │ ├── AnonymousDisposable.swift │ │ │ ├── BinaryDisposable.swift │ │ │ ├── BooleanDisposable.swift │ │ │ ├── CompositeDisposable.swift │ │ │ ├── Disposables.swift │ │ │ ├── DisposeBag.swift │ │ │ ├── DisposeBase.swift │ │ │ ├── NopDisposable.swift │ │ │ ├── RefCountDisposable.swift │ │ │ ├── ScheduledDisposable.swift │ │ │ ├── SerialDisposable.swift │ │ │ ├── SingleAssignmentDisposable.swift │ │ │ └── SubscriptionDisposable.swift │ │ ├── Errors.swift │ │ ├── Event.swift │ │ ├── Extensions/ │ │ │ ├── Bag+Rx.swift │ │ │ └── String+Rx.swift │ │ ├── GroupedObservable.swift │ │ ├── ImmediateSchedulerType.swift │ │ ├── Observable.swift │ │ ├── ObservableConvertibleType.swift │ │ ├── ObservableType+Extensions.swift │ │ ├── ObservableType.swift │ │ ├── Observables/ │ │ │ ├── AddRef.swift │ │ │ ├── Amb.swift │ │ │ ├── AsMaybe.swift │ │ │ ├── AsSingle.swift │ │ │ ├── Buffer.swift │ │ │ ├── Catch.swift │ │ │ ├── CombineLatest+Collection.swift │ │ │ ├── CombineLatest+arity.swift │ │ │ ├── CombineLatest.swift │ │ │ ├── Concat.swift │ │ │ ├── Create.swift │ │ │ ├── Debounce.swift │ │ │ ├── Debug.swift │ │ │ ├── DefaultIfEmpty.swift │ │ │ ├── Deferred.swift │ │ │ ├── Delay.swift │ │ │ ├── DelaySubscription.swift │ │ │ ├── Dematerialize.swift │ │ │ ├── DistinctUntilChanged.swift │ │ │ ├── Do.swift │ │ │ ├── ElementAt.swift │ │ │ ├── Empty.swift │ │ │ ├── Enumerated.swift │ │ │ ├── Error.swift │ │ │ ├── Filter.swift │ │ │ ├── First.swift │ │ │ ├── Generate.swift │ │ │ ├── GroupBy.swift │ │ │ ├── Just.swift │ │ │ ├── Map.swift │ │ │ ├── Materialize.swift │ │ │ ├── Merge.swift │ │ │ ├── Multicast.swift │ │ │ ├── Never.swift │ │ │ ├── ObserveOn.swift │ │ │ ├── Optional.swift │ │ │ ├── Producer.swift │ │ │ ├── Range.swift │ │ │ ├── Reduce.swift │ │ │ ├── Repeat.swift │ │ │ ├── RetryWhen.swift │ │ │ ├── Sample.swift │ │ │ ├── Scan.swift │ │ │ ├── Sequence.swift │ │ │ ├── ShareReplayScope.swift │ │ │ ├── SingleAsync.swift │ │ │ ├── Sink.swift │ │ │ ├── Skip.swift │ │ │ ├── SkipUntil.swift │ │ │ ├── SkipWhile.swift │ │ │ ├── StartWith.swift │ │ │ ├── SubscribeOn.swift │ │ │ ├── Switch.swift │ │ │ ├── SwitchIfEmpty.swift │ │ │ ├── Take.swift │ │ │ ├── TakeLast.swift │ │ │ ├── TakeUntil.swift │ │ │ ├── TakeWhile.swift │ │ │ ├── Throttle.swift │ │ │ ├── Timeout.swift │ │ │ ├── Timer.swift │ │ │ ├── ToArray.swift │ │ │ ├── Using.swift │ │ │ ├── Window.swift │ │ │ ├── WithLatestFrom.swift │ │ │ ├── Zip+Collection.swift │ │ │ ├── Zip+arity.swift │ │ │ └── Zip.swift │ │ ├── ObserverType.swift │ │ ├── Observers/ │ │ │ ├── AnonymousObserver.swift │ │ │ ├── ObserverBase.swift │ │ │ └── TailRecursiveSink.swift │ │ ├── Reactive.swift │ │ ├── Rx.swift │ │ ├── RxMutableBox.swift │ │ ├── SchedulerType.swift │ │ ├── Schedulers/ │ │ │ ├── ConcurrentDispatchQueueScheduler.swift │ │ │ ├── ConcurrentMainScheduler.swift │ │ │ ├── CurrentThreadScheduler.swift │ │ │ ├── HistoricalScheduler.swift │ │ │ ├── HistoricalSchedulerTimeConverter.swift │ │ │ ├── Internal/ │ │ │ │ ├── DispatchQueueConfiguration.swift │ │ │ │ ├── InvocableScheduledItem.swift │ │ │ │ ├── InvocableType.swift │ │ │ │ ├── ScheduledItem.swift │ │ │ │ └── ScheduledItemType.swift │ │ │ ├── MainScheduler.swift │ │ │ ├── OperationQueueScheduler.swift │ │ │ ├── RecursiveScheduler.swift │ │ │ ├── SchedulerServices+Emulation.swift │ │ │ ├── SerialDispatchQueueScheduler.swift │ │ │ ├── VirtualTimeConverterType.swift │ │ │ └── VirtualTimeScheduler.swift │ │ ├── Subjects/ │ │ │ ├── AsyncSubject.swift │ │ │ ├── BehaviorSubject.swift │ │ │ ├── PublishSubject.swift │ │ │ ├── ReplaySubject.swift │ │ │ └── SubjectType.swift │ │ ├── SwiftSupport/ │ │ │ └── SwiftSupport.swift │ │ └── Traits/ │ │ ├── Completable+AndThen.swift │ │ ├── Completable.swift │ │ ├── Maybe.swift │ │ ├── ObservableType+PrimitiveSequence.swift │ │ ├── PrimitiveSequence+Zip+arity.swift │ │ ├── PrimitiveSequence.swift │ │ └── Single.swift │ ├── SnapKit/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Source/ │ │ ├── Constraint.swift │ │ ├── ConstraintAttributes.swift │ │ ├── ConstraintConfig.swift │ │ ├── ConstraintConstantTarget.swift │ │ ├── ConstraintDSL.swift │ │ ├── ConstraintDescription.swift │ │ ├── ConstraintInsetTarget.swift │ │ ├── ConstraintInsets.swift │ │ ├── ConstraintItem.swift │ │ ├── ConstraintLayoutGuide+Extensions.swift │ │ ├── ConstraintLayoutGuide.swift │ │ ├── ConstraintLayoutGuideDSL.swift │ │ ├── ConstraintLayoutSupport.swift │ │ ├── ConstraintLayoutSupportDSL.swift │ │ ├── ConstraintMaker.swift │ │ ├── ConstraintMakerEditable.swift │ │ ├── ConstraintMakerExtendable.swift │ │ ├── ConstraintMakerFinalizable.swift │ │ ├── ConstraintMakerPriortizable.swift │ │ ├── ConstraintMakerRelatable.swift │ │ ├── ConstraintMultiplierTarget.swift │ │ ├── ConstraintOffsetTarget.swift │ │ ├── ConstraintPriority.swift │ │ ├── ConstraintPriorityTarget.swift │ │ ├── ConstraintRelatableTarget.swift │ │ ├── ConstraintRelation.swift │ │ ├── ConstraintView+Extensions.swift │ │ ├── ConstraintView.swift │ │ ├── ConstraintViewDSL.swift │ │ ├── Debugging.swift │ │ ├── LayoutConstraint.swift │ │ ├── LayoutConstraintItem.swift │ │ ├── Typealiases.swift │ │ └── UILayoutSupport+Extensions.swift │ ├── SwiftyColor/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources/ │ │ └── SwiftyColor.swift │ ├── SwiftyJSON/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Source/ │ │ └── SwiftyJSON.swift │ ├── TTRangeSlider/ │ │ ├── LICENSE │ │ ├── Pod/ │ │ │ └── Classes/ │ │ │ ├── TTRangeSlider.h │ │ │ ├── TTRangeSlider.m │ │ │ └── TTRangeSliderDelegate.h │ │ └── README.md │ ├── TYCyclePagerView/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── TYCyclePagerViewDemo/ │ │ └── TYCyclePagerView/ │ │ ├── TYCyclePagerTransformLayout.h │ │ ├── TYCyclePagerTransformLayout.m │ │ ├── TYCyclePagerView.h │ │ ├── TYCyclePagerView.m │ │ ├── TYPageControl.h │ │ └── TYPageControl.m │ ├── TYPagerController/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── TYPagerControllerDemo/ │ │ └── TYPagerController/ │ │ ├── TYPagerController.h │ │ ├── TYPagerController.m │ │ ├── TYPagerView.h │ │ ├── TYPagerView.m │ │ ├── TYPagerViewLayout.h │ │ ├── TYPagerViewLayout.m │ │ └── TabPager/ │ │ ├── TYTabPagerBar.h │ │ ├── TYTabPagerBar.m │ │ ├── TYTabPagerBarCell.h │ │ ├── TYTabPagerBarCell.m │ │ ├── TYTabPagerBarLayout.h │ │ ├── TYTabPagerBarLayout.m │ │ ├── TYTabPagerController.h │ │ ├── TYTabPagerController.m │ │ ├── TYTabPagerView.h │ │ └── TYTabPagerView.m │ ├── Target Support Files/ │ │ ├── Alamofire/ │ │ │ ├── Alamofire-dummy.m │ │ │ ├── Alamofire-prefix.pch │ │ │ ├── Alamofire-umbrella.h │ │ │ ├── Alamofire.modulemap │ │ │ ├── Alamofire.xcconfig │ │ │ └── Info.plist │ │ ├── Differentiator/ │ │ │ ├── Differentiator-dummy.m │ │ │ ├── Differentiator-prefix.pch │ │ │ ├── Differentiator-umbrella.h │ │ │ ├── Differentiator.modulemap │ │ │ ├── Differentiator.xcconfig │ │ │ └── Info.plist │ │ ├── IQKeyboardManagerSwift/ │ │ │ ├── IQKeyboardManagerSwift-dummy.m │ │ │ ├── IQKeyboardManagerSwift-prefix.pch │ │ │ ├── IQKeyboardManagerSwift-umbrella.h │ │ │ ├── IQKeyboardManagerSwift.modulemap │ │ │ ├── IQKeyboardManagerSwift.xcconfig │ │ │ └── Info.plist │ │ ├── Kingfisher/ │ │ │ ├── Info.plist │ │ │ ├── Kingfisher-dummy.m │ │ │ ├── Kingfisher-prefix.pch │ │ │ ├── Kingfisher-umbrella.h │ │ │ ├── Kingfisher.modulemap │ │ │ └── Kingfisher.xcconfig │ │ ├── MJRefresh/ │ │ │ ├── Info.plist │ │ │ ├── MJRefresh-dummy.m │ │ │ ├── MJRefresh-prefix.pch │ │ │ ├── MJRefresh-umbrella.h │ │ │ ├── MJRefresh.modulemap │ │ │ └── MJRefresh.xcconfig │ │ ├── Moya/ │ │ │ ├── Info.plist │ │ │ ├── Moya-dummy.m │ │ │ ├── Moya-prefix.pch │ │ │ ├── Moya-umbrella.h │ │ │ ├── Moya.modulemap │ │ │ └── Moya.xcconfig │ │ ├── NSObject+Rx/ │ │ │ ├── Info.plist │ │ │ ├── NSObject+Rx-dummy.m │ │ │ ├── NSObject+Rx-prefix.pch │ │ │ ├── NSObject+Rx-umbrella.h │ │ │ ├── NSObject+Rx.modulemap │ │ │ └── NSObject+Rx.xcconfig │ │ ├── ObjectMapper/ │ │ │ ├── Info.plist │ │ │ ├── ObjectMapper-dummy.m │ │ │ ├── ObjectMapper-prefix.pch │ │ │ ├── ObjectMapper-umbrella.h │ │ │ ├── ObjectMapper.modulemap │ │ │ └── ObjectMapper.xcconfig │ │ ├── Pods-RxXMLY/ │ │ │ ├── Info.plist │ │ │ ├── Pods-RxXMLY-acknowledgements.markdown │ │ │ ├── Pods-RxXMLY-acknowledgements.plist │ │ │ ├── Pods-RxXMLY-dummy.m │ │ │ ├── Pods-RxXMLY-frameworks.sh │ │ │ ├── Pods-RxXMLY-resources.sh │ │ │ ├── Pods-RxXMLY-umbrella.h │ │ │ ├── Pods-RxXMLY.debug.xcconfig │ │ │ ├── Pods-RxXMLY.modulemap │ │ │ └── Pods-RxXMLY.release.xcconfig │ │ ├── ReactorKit/ │ │ │ ├── Info.plist │ │ │ ├── ReactorKit-dummy.m │ │ │ ├── ReactorKit-prefix.pch │ │ │ ├── ReactorKit-umbrella.h │ │ │ ├── ReactorKit.modulemap │ │ │ └── ReactorKit.xcconfig │ │ ├── Result/ │ │ │ ├── Info.plist │ │ │ ├── Result-dummy.m │ │ │ ├── Result-prefix.pch │ │ │ ├── Result-umbrella.h │ │ │ ├── Result.modulemap │ │ │ └── Result.xcconfig │ │ ├── ReusableKit/ │ │ │ ├── Info.plist │ │ │ ├── ReusableKit-dummy.m │ │ │ ├── ReusableKit-prefix.pch │ │ │ ├── ReusableKit-umbrella.h │ │ │ ├── ReusableKit.modulemap │ │ │ └── ReusableKit.xcconfig │ │ ├── RxAlamofire/ │ │ │ ├── Info.plist │ │ │ ├── RxAlamofire-dummy.m │ │ │ ├── RxAlamofire-prefix.pch │ │ │ ├── RxAlamofire-umbrella.h │ │ │ ├── RxAlamofire.modulemap │ │ │ └── RxAlamofire.xcconfig │ │ ├── RxCocoa/ │ │ │ ├── Info.plist │ │ │ ├── RxCocoa-dummy.m │ │ │ ├── RxCocoa-prefix.pch │ │ │ ├── RxCocoa-umbrella.h │ │ │ ├── RxCocoa.modulemap │ │ │ └── RxCocoa.xcconfig │ │ ├── RxDataSources/ │ │ │ ├── Info.plist │ │ │ ├── RxDataSources-dummy.m │ │ │ ├── RxDataSources-prefix.pch │ │ │ ├── RxDataSources-umbrella.h │ │ │ ├── RxDataSources.modulemap │ │ │ └── RxDataSources.xcconfig │ │ ├── RxGesture/ │ │ │ ├── Info.plist │ │ │ ├── RxGesture-dummy.m │ │ │ ├── RxGesture-prefix.pch │ │ │ ├── RxGesture-umbrella.h │ │ │ ├── RxGesture.modulemap │ │ │ └── RxGesture.xcconfig │ │ ├── RxSwift/ │ │ │ ├── Info.plist │ │ │ ├── RxSwift-dummy.m │ │ │ ├── RxSwift-prefix.pch │ │ │ ├── RxSwift-umbrella.h │ │ │ ├── RxSwift.modulemap │ │ │ └── RxSwift.xcconfig │ │ ├── SnapKit/ │ │ │ ├── Info.plist │ │ │ ├── SnapKit-dummy.m │ │ │ ├── SnapKit-prefix.pch │ │ │ ├── SnapKit-umbrella.h │ │ │ ├── SnapKit.modulemap │ │ │ └── SnapKit.xcconfig │ │ ├── SwiftyColor/ │ │ │ ├── Info.plist │ │ │ ├── SwiftyColor-dummy.m │ │ │ ├── SwiftyColor-prefix.pch │ │ │ ├── SwiftyColor-umbrella.h │ │ │ ├── SwiftyColor.modulemap │ │ │ └── SwiftyColor.xcconfig │ │ ├── SwiftyJSON/ │ │ │ ├── Info.plist │ │ │ ├── SwiftyJSON-dummy.m │ │ │ ├── SwiftyJSON-prefix.pch │ │ │ ├── SwiftyJSON-umbrella.h │ │ │ ├── SwiftyJSON.modulemap │ │ │ └── SwiftyJSON.xcconfig │ │ ├── TTRangeSlider/ │ │ │ ├── Info.plist │ │ │ ├── TTRangeSlider-dummy.m │ │ │ ├── TTRangeSlider-prefix.pch │ │ │ ├── TTRangeSlider-umbrella.h │ │ │ ├── TTRangeSlider.modulemap │ │ │ └── TTRangeSlider.xcconfig │ │ ├── TYCyclePagerView/ │ │ │ ├── Info.plist │ │ │ ├── TYCyclePagerView-dummy.m │ │ │ ├── TYCyclePagerView-prefix.pch │ │ │ ├── TYCyclePagerView-umbrella.h │ │ │ ├── TYCyclePagerView.modulemap │ │ │ └── TYCyclePagerView.xcconfig │ │ ├── TYPagerController/ │ │ │ ├── Info.plist │ │ │ ├── TYPagerController-dummy.m │ │ │ ├── TYPagerController-prefix.pch │ │ │ ├── TYPagerController-umbrella.h │ │ │ ├── TYPagerController.modulemap │ │ │ └── TYPagerController.xcconfig │ │ ├── Then/ │ │ │ ├── Info.plist │ │ │ ├── Then-dummy.m │ │ │ ├── Then-prefix.pch │ │ │ ├── Then-umbrella.h │ │ │ ├── Then.modulemap │ │ │ └── Then.xcconfig │ │ └── URLNavigator/ │ │ ├── Info.plist │ │ ├── URLNavigator-dummy.m │ │ ├── URLNavigator-prefix.pch │ │ ├── URLNavigator-umbrella.h │ │ ├── URLNavigator.modulemap │ │ └── URLNavigator.xcconfig │ ├── Then/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources/ │ │ └── Then/ │ │ └── Then.swift │ └── URLNavigator/ │ ├── LICENSE │ ├── README.md │ └── Sources/ │ ├── URLMatcher/ │ │ ├── URLConvertible.swift │ │ ├── URLMatchResult.swift │ │ ├── URLMatcher.swift │ │ ├── URLPatchComponentMatchResult.swift │ │ └── URLPathComponent.swift │ └── URLNavigator/ │ ├── Navigator.swift │ ├── NavigatorDelegate.swift │ ├── NavigatorType.swift │ ├── UIViewController+TopMostViewController.swift │ └── UIViewControllerType.swift ├── README.md ├── RxXMLY/ │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Default.imageset/ │ │ │ └── Contents.json │ │ ├── favicon.imageset/ │ │ │ └── Contents.json │ │ └── icon_XMLY/ │ │ ├── +.imageset/ │ │ │ └── Contents.json │ │ ├── -.imageset/ │ │ │ └── Contents.json │ │ ├── 3dNeedHeadSet.imageset/ │ │ │ └── Contents.json │ │ ├── Ad_tag_inbanner.imageset/ │ │ │ └── Contents.json │ │ ├── Ad_tag_incell.imageset/ │ │ │ └── Contents.json │ │ ├── Ad_tag_infeed.imageset/ │ │ │ └── Contents.json │ │ ├── Album_toast_hint_up.imageset/ │ │ │ └── Contents.json │ │ ├── AlipaySDK/ │ │ │ ├── Contents.json │ │ │ ├── bar.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── refresh.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── refresh_click.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── shutdown.imageset/ │ │ │ │ └── Contents.json │ │ │ └── shutdown_click.imageset/ │ │ │ └── Contents.json │ │ ├── Assets.dataset/ │ │ │ ├── Assets.car │ │ │ └── Contents.json │ │ ├── Cancel_btn.imageset/ │ │ │ └── Contents.json │ │ ├── Combined Shape.imageset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── CorpV0.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV1.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV10.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV11.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV12.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV13.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV14.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV15.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV16.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV2.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV3.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV4.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV5.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV6.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV7.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV8.imageset/ │ │ │ └── Contents.json │ │ ├── CorpV9.imageset/ │ │ │ └── Contents.json │ │ ├── DINCondensed Bold.dataset/ │ │ │ └── Contents.json │ │ ├── DSDigitalNormal.dataset/ │ │ │ └── Contents.json │ │ ├── Group 2.imageset/ │ │ │ └── Contents.json │ │ ├── Group 25.imageset/ │ │ │ └── Contents.json │ │ ├── Group 3 Copy.imageset/ │ │ │ └── Contents.json │ │ ├── Group 7 Copy 3.imageset/ │ │ │ └── Contents.json │ │ ├── Group_bg_list.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_audio_left_gif1.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_audio_left_gif2.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_audio_left_gif3.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_audio_right_gif1.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_audio_right_gif2.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_audio_right_gif3.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_delete.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_edit.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_scteening.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talk_cancel.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talk_warning.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talkon_gif1.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talkon_gif2.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talkon_gif3.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talkon_gif4.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talkon_gif5.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_talkon_gif6.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_warning.imageset/ │ │ │ └── Contents.json │ │ ├── Group_ic_x.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_>.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_announcement.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_close.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_delete.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_establish.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_get.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_manager.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_more.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_moreMsg.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_moregroup_n.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_moregroup_p.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_owner.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_pic.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_plus.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_plus_add_members.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_plus_d.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_plus_n.imageset/ │ │ │ └── Contents.json │ │ ├── Group_icon_topic.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_Q&A.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_congratulations.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_hi.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_meiyouqunzu.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_notice.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_picloading.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_recruit.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_sayhi.imageset/ │ │ │ └── Contents.json │ │ ├── Group_img_topic.imageset/ │ │ │ └── Contents.json │ │ ├── ID5_lockbg.imageset/ │ │ │ └── Contents.json │ │ ├── ID5_skip_normal.imageset/ │ │ │ └── Contents.json │ │ ├── ID5_skip_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── JSNativeRecoder_upload_bg.imageset/ │ │ │ └── Contents.json │ │ ├── Launching.imageset/ │ │ │ └── Contents.json │ │ ├── MCArrow.imageset/ │ │ │ └── Contents.json │ │ ├── MCLive.imageset/ │ │ │ └── Contents.json │ │ ├── MCRecord.imageset/ │ │ │ └── Contents.json │ │ ├── My_ic_massage.imageset/ │ │ │ └── Contents.json │ │ ├── My_icon_RSS.imageset/ │ │ │ └── Contents.json │ │ ├── My_icon_download.imageset/ │ │ │ └── Contents.json │ │ ├── My_icon_history.imageset/ │ │ │ └── Contents.json │ │ ├── My_icon_tgk.imageset/ │ │ │ └── Contents.json │ │ ├── My_jmgl_ic_a+.imageset/ │ │ │ └── Contents.json │ │ ├── My_more_icon_spread.imageset/ │ │ │ └── Contents.json │ │ ├── NPCreateComment.imageset/ │ │ │ └── Contents.json │ │ ├── NPIPauseStatus.imageset/ │ │ │ └── Contents.json │ │ ├── NPIPlayStatus.imageset/ │ │ │ └── Contents.json │ │ ├── NPIShareMore.imageset/ │ │ │ └── Contents.json │ │ ├── NPIShareWeChat.imageset/ │ │ │ └── Contents.json │ │ ├── NPIShareWeChatF.imageset/ │ │ │ └── Contents.json │ │ ├── NPIShareWeibo.imageset/ │ │ │ └── Contents.json │ │ ├── NPProCycle.imageset/ │ │ │ └── Contents.json │ │ ├── NPProCycleHL.imageset/ │ │ │ └── Contents.json │ │ ├── NPProDMOff.imageset/ │ │ │ └── Contents.json │ │ ├── NPProDMOffHL.imageset/ │ │ │ └── Contents.json │ │ ├── NPProDMOn.imageset/ │ │ │ └── Contents.json │ │ ├── NPProDMOnHL.imageset/ │ │ │ └── Contents.json │ │ ├── NPProList.imageset/ │ │ │ └── Contents.json │ │ ├── NPProListHL.imageset/ │ │ │ └── Contents.json │ │ ├── NPProRandom.imageset/ │ │ │ └── Contents.json │ │ ├── NPProRandomHL.imageset/ │ │ │ └── Contents.json │ │ ├── NPProSet.imageset/ │ │ │ └── Contents.json │ │ ├── NPProSetCmt.imageset/ │ │ │ └── Contents.json │ │ ├── NPProSetHL.imageset/ │ │ │ └── Contents.json │ │ ├── NPProSingle.imageset/ │ │ │ └── Contents.json │ │ ├── NPProSingleHL.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeArrow.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV0.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV1.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV10.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV11.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV12.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV13.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV14.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV15.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV16.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV2.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV3.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV4.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV5.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV6.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV7.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV8.imageset/ │ │ │ └── Contents.json │ │ ├── PHGradeV9.imageset/ │ │ │ └── Contents.json │ │ ├── PkgInfo.dataset/ │ │ │ ├── Contents.json │ │ │ └── PkgInfo │ │ ├── PushNotificationPayload.dataset/ │ │ │ ├── Contents.json │ │ │ └── PushNotificationPayload.apns │ │ ├── QA4Free.imageset/ │ │ │ └── Contents.json │ │ ├── QA4Pay.imageset/ │ │ │ └── Contents.json │ │ ├── QAIntroBack.imageset/ │ │ │ └── Contents.json │ │ ├── QALogo.imageset/ │ │ │ └── Contents.json │ │ ├── QAVolume.imageset/ │ │ │ └── Contents.json │ │ ├── README.dataset/ │ │ │ ├── Contents.json │ │ │ └── README.txt │ │ ├── RFGuideClose.imageset/ │ │ │ └── Contents.json │ │ ├── RF_history.imageset/ │ │ │ └── Contents.json │ │ ├── RF_hot_category.imageset/ │ │ │ └── Contents.json │ │ ├── RF_icon_loading.imageset/ │ │ │ └── Contents.json │ │ ├── RF_local.imageset/ │ │ │ └── Contents.json │ │ ├── RF_news.imageset/ │ │ │ └── Contents.json │ │ ├── RF_noContent.imageset/ │ │ │ └── Contents.json │ │ ├── RF_noInternet.imageset/ │ │ │ └── Contents.json │ │ ├── RF_normal_level.imageset/ │ │ │ └── Contents.json │ │ ├── RF_other.imageset/ │ │ │ └── Contents.json │ │ ├── Star.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_Album_Section_Image.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_Answere_Section_Image.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_Rights_Section_Image.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_card_BG_default.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_cell_BG_img.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_cell_small_BG_img.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_right_Image.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_split_Image.imageset/ │ │ │ └── Contents.json │ │ ├── VIP_success_big.imageset/ │ │ │ └── Contents.json │ │ ├── Wifi_rock.imageset/ │ │ │ └── Contents.json │ │ ├── Wifi_rock_pwd.imageset/ │ │ │ └── Contents.json │ │ ├── XMPaySuccessView.dataset/ │ │ │ ├── Contents.json │ │ │ └── XMPaySuccessView.nib │ │ ├── XMRefundInfoView.dataset/ │ │ │ ├── Contents.json │ │ │ └── XMRefundInfoView.nib │ │ ├── XMUIKit/ │ │ │ ├── Contents.json │ │ │ ├── Info.dataset/ │ │ │ │ ├── Contents.json │ │ │ │ └── Info.plist │ │ │ ├── pullToRefresh_0.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_1.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_2.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_3.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_4.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_5.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_6.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_7.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_8.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── pullToRefresh_9.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── xmb_back_h.imageset/ │ │ │ │ └── Contents.json │ │ │ └── xmb_back_highlight.imageset/ │ │ │ └── Contents.json │ │ ├── XNLocalizable.dataset/ │ │ │ ├── Contents.json │ │ │ └── XNLocalizable.strings │ │ ├── abc_bg_comments.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_emoji.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_gallery_camera.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_gallery_check.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_gallery_checked.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_gallery_select.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_gallery_selected.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_keyboard.imageset/ │ │ │ └── Contents.json │ │ ├── abc_btn_talk.imageset/ │ │ │ └── Contents.json │ │ ├── abc_ic_notice_live.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_TF.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_comment.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_default.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_download.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_fans.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_history.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_like.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_network.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_search.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_sounds.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_subscribe.imageset/ │ │ │ └── Contents.json │ │ ├── abc_img_no_toplist.imageset/ │ │ │ └── Contents.json │ │ ├── about_icon.imageset/ │ │ │ └── Contents.json │ │ ├── abq_lockscreen.imageset/ │ │ │ └── Contents.json │ │ ├── activity_begin.imageset/ │ │ │ └── Contents.json │ │ ├── activity_during.imageset/ │ │ │ └── Contents.json │ │ ├── activity_end.imageset/ │ │ │ └── Contents.json │ │ ├── ad_icon_close.imageset/ │ │ │ └── Contents.json │ │ ├── ad_img_logo.imageset/ │ │ │ └── Contents.json │ │ ├── ad_soundPatch_image.imageset/ │ │ │ └── Contents.json │ │ ├── ad_toolbar_pause_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── ad_toolbar_play_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── add.imageset/ │ │ │ └── Contents.json │ │ ├── addphoto_icon.imageset/ │ │ │ └── Contents.json │ │ ├── addphoto_icon_disable.imageset/ │ │ │ └── Contents.json │ │ ├── addphoto_icon_h.imageset/ │ │ │ └── Contents.json │ │ ├── addphoto_kb.imageset/ │ │ │ └── Contents.json │ │ ├── airPlay.imageset/ │ │ │ └── Contents.json │ │ ├── albumArrow.imageset/ │ │ │ └── Contents.json │ │ ├── albumHeaderDefault.imageset/ │ │ │ └── Contents.json │ │ ├── albumSubscribe.imageset/ │ │ │ └── Contents.json │ │ ├── albumView_top_bg.imageset/ │ │ │ └── Contents.json │ │ ├── album_activitytag_default.imageset/ │ │ │ └── Contents.json │ │ ├── album_album_mask.imageset/ │ │ │ └── Contents.json │ │ ├── album_blind.imageset/ │ │ │ └── Contents.json │ │ ├── album_change_btn.imageset/ │ │ │ └── Contents.json │ │ ├── album_collected.imageset/ │ │ │ └── Contents.json │ │ ├── album_collected_non.imageset/ │ │ │ └── Contents.json │ │ ├── album_coupon_left.imageset/ │ │ │ └── Contents.json │ │ ├── album_coupon_right.imageset/ │ │ │ └── Contents.json │ │ ├── album_cover_bg.imageset/ │ │ │ └── Contents.json │ │ ├── album_dashed.imageset/ │ │ │ └── Contents.json │ │ ├── album_down_similar.imageset/ │ │ │ └── Contents.json │ │ ├── album_download.imageset/ │ │ │ └── Contents.json │ │ ├── album_mecoupon_right.imageset/ │ │ │ └── Contents.json │ │ ├── album_multiDownload.imageset/ │ │ │ └── Contents.json │ │ ├── album_novel_finish.imageset/ │ │ │ └── Contents.json │ │ ├── album_pay_refund.imageset/ │ │ │ └── Contents.json │ │ ├── album_play.imageset/ │ │ │ └── Contents.json │ │ ├── album_playCountLogo.imageset/ │ │ │ └── Contents.json │ │ ├── album_plus_icon.imageset/ │ │ │ └── Contents.json │ │ ├── album_private.imageset/ │ │ │ └── Contents.json │ │ ├── album_rec_unlike.imageset/ │ │ │ └── Contents.json │ │ ├── album_rec_unlike_bg_h.imageset/ │ │ │ └── Contents.json │ │ ├── album_rec_unlike_bg_indicator.imageset/ │ │ │ └── Contents.json │ │ ├── album_rec_unlike_bg_indicator_down.imageset/ │ │ │ └── Contents.json │ │ ├── album_rec_unlike_bg_n.imageset/ │ │ │ └── Contents.json │ │ ├── album_relative.imageset/ │ │ │ └── Contents.json │ │ ├── album_relative_album.imageset/ │ │ │ └── Contents.json │ │ ├── album_removed.imageset/ │ │ │ └── Contents.json │ │ ├── album_rightArrow.imageset/ │ │ │ └── Contents.json │ │ ├── album_search.imageset/ │ │ │ └── Contents.json │ │ ├── album_segButton.imageset/ │ │ │ └── Contents.json │ │ ├── album_sort.imageset/ │ │ │ └── Contents.json │ │ ├── album_sortButton.imageset/ │ │ │ └── Contents.json │ │ ├── album_sort_down.imageset/ │ │ │ └── Contents.json │ │ ├── album_subscribe_count.imageset/ │ │ │ └── Contents.json │ │ ├── album_tag_delete.imageset/ │ │ │ └── Contents.json │ │ ├── album_tag_normal.imageset/ │ │ │ └── Contents.json │ │ ├── album_tag_selected.imageset/ │ │ │ └── Contents.json │ │ ├── album_tracks.imageset/ │ │ │ └── Contents.json │ │ ├── album_unlike.imageset/ │ │ │ └── Contents.json │ │ ├── album_vip_guide_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── album_vip_guide_bg.imageset/ │ │ │ └── Contents.json │ │ ├── albumpage_audition_ic.imageset/ │ │ │ └── Contents.json │ │ ├── albumpage_gift_ic.imageset/ │ │ │ └── Contents.json │ │ ├── albumpage_subscribed_ic.imageset/ │ │ │ └── Contents.json │ │ ├── albumpage_subscription_ic.imageset/ │ │ │ └── Contents.json │ │ ├── albumtag_pay.imageset/ │ │ │ └── Contents.json │ │ ├── allSing.imageset/ │ │ │ └── Contents.json │ │ ├── allSingMic.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_buy_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_buy_2.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_group_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_group_2.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_group_red_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_group_red_2.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_pause_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_pause_2.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_play_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_play_2.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_share_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_share_2.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_subscribe_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_subscribe_2.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_subscribed_1.imageset/ │ │ │ └── Contents.json │ │ ├── alum_ic_subscribed_2.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_gift.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_gift_line.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_tips.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_top1.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_top2.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_top3.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_topanchor.imageset/ │ │ │ └── Contents.json │ │ ├── anchortop_ic_weektop.imageset/ │ │ │ └── Contents.json │ │ ├── archived-expanded-entitlements.dataset/ │ │ │ ├── Contents.json │ │ │ └── archived-expanded-entitlements.xcent │ │ ├── arrow_down.imageset/ │ │ │ └── Contents.json │ │ ├── arrow_ic.imageset/ │ │ │ └── Contents.json │ │ ├── arrow_up.imageset/ │ │ │ └── Contents.json │ │ ├── audio_AD_close.imageset/ │ │ │ └── Contents.json │ │ ├── audition_finish.imageset/ │ │ │ └── Contents.json │ │ ├── autoShutDown.imageset/ │ │ │ └── Contents.json │ │ ├── avatar_bg.imageset/ │ │ │ └── Contents.json │ │ ├── badge_num.imageset/ │ │ │ └── Contents.json │ │ ├── badge_num1.imageset/ │ │ │ └── Contents.json │ │ ├── badge_num2.imageset/ │ │ │ └── Contents.json │ │ ├── bg_albumView_header.imageset/ │ │ │ └── Contents.json │ │ ├── bg_album_detail_text.imageset/ │ │ │ └── Contents.json │ │ ├── bg_carlife.imageset/ │ │ │ └── Contents.json │ │ ├── bg_find_segsel.imageset/ │ │ │ └── Contents.json │ │ ├── bg_focusImage_loading.imageset/ │ │ │ └── Contents.json │ │ ├── bg_live_date.imageset/ │ │ │ └── Contents.json │ │ ├── bg_msgbox_1.imageset/ │ │ │ └── Contents.json │ │ ├── bg_msgbox_2.imageset/ │ │ │ └── Contents.json │ │ ├── bg_msgbox_3.imageset/ │ │ │ └── Contents.json │ │ ├── bg_statusbar.imageset/ │ │ │ └── Contents.json │ │ ├── bg_transparent.imageset/ │ │ │ └── Contents.json │ │ ├── bg_wakeup.imageset/ │ │ │ └── Contents.json │ │ ├── bg_wave.imageset/ │ │ │ └── Contents.json │ │ ├── bgm_btn_checked.imageset/ │ │ │ └── Contents.json │ │ ├── bgm_btn_downloaded.imageset/ │ │ │ └── Contents.json │ │ ├── bgm_btn_unchecked.imageset/ │ │ │ └── Contents.json │ │ ├── bgm_tab_btn.imageset/ │ │ │ └── Contents.json │ │ ├── bindMphoneWarning.imageset/ │ │ │ └── Contents.json │ │ ├── bindPhone.imageset/ │ │ │ └── Contents.json │ │ ├── bitingCover.imageset/ │ │ │ └── Contents.json │ │ ├── bitingMore.imageset/ │ │ │ └── Contents.json │ │ ├── bitingPlayAll.imageset/ │ │ │ └── Contents.json │ │ ├── biting_bg.imageset/ │ │ │ └── Contents.json │ │ ├── biting_slogen.imageset/ │ │ │ └── Contents.json │ │ ├── bmw_connected.imageset/ │ │ │ └── Contents.json │ │ ├── bmw_connected_logo.imageset/ │ │ │ └── Contents.json │ │ ├── bmw_lock_line.imageset/ │ │ │ └── Contents.json │ │ ├── bmw_lockbg.imageset/ │ │ │ └── Contents.json │ │ ├── bmw_skip_normal.imageset/ │ │ │ └── Contents.json │ │ ├── bmw_skip_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── bofangtiao_l.imageset/ │ │ │ └── Contents.json │ │ ├── bofangtiao_r.imageset/ │ │ │ └── Contents.json │ │ ├── boom.dataset/ │ │ │ ├── Contents.json │ │ │ └── boom.json │ │ ├── bottomBg.imageset/ │ │ │ └── Contents.json │ │ ├── brand_icon.imageset/ │ │ │ └── Contents.json │ │ ├── brand_icon_unavailable.imageset/ │ │ │ └── Contents.json │ │ ├── btfmbg.imageset/ │ │ │ └── Contents.json │ │ ├── btlogo.imageset/ │ │ │ └── Contents.json │ │ ├── btn_activitybg_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_activitybg_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_back_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_back_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_gold.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_green.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_grey.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_grey2.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_light.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_lightGray.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_orange.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_orange2.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_t.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_unable.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_w.imageset/ │ │ │ └── Contents.json │ │ ├── btn_bg_yellow.imageset/ │ │ │ └── Contents.json │ │ ├── btn_block_remove_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_block_remove_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_buy_bg.imageset/ │ │ │ └── Contents.json │ │ ├── btn_cancel_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_cancel_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_clear_listened_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_clear_listened_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_close_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_close_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_codeNext_d.imageset/ │ │ │ └── Contents.json │ │ ├── btn_codeNext_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_codeNext_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_comment.imageset/ │ │ │ └── Contents.json │ │ ├── btn_del_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_dmgg_bg.imageset/ │ │ │ └── Contents.json │ │ ├── btn_dmgg_bg_o.imageset/ │ │ │ └── Contents.json │ │ ├── btn_dmgg_buling.imageset/ │ │ │ └── Contents.json │ │ ├── btn_done_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_done_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadAlbumSound_list_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadAlbumSound_savelist_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadalbum_delete_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_ascOrder_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_clear_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_clear_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_continue_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_continue_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_deletion_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_list_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_list_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_pause_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_pause_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_reverseOrder_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_savelist_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_downloadsound_savelist_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_fav.imageset/ │ │ │ └── Contents.json │ │ ├── btn_feedblock_remove_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_feedblock_remove_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_finish_listened_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_finish_listened_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_follow_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_home_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_home_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_indicator.imageset/ │ │ │ └── Contents.json │ │ ├── btn_invite.imageset/ │ │ │ └── Contents.json │ │ ├── btn_invite_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_join_vip.imageset/ │ │ │ └── Contents.json │ │ ├── btn_login_action_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_login_action_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_mailCode_submit_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_mailCode_submit_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_mention_bg.imageset/ │ │ │ └── Contents.json │ │ ├── btn_mention_bg_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_more_down.imageset/ │ │ │ └── Contents.json │ │ ├── btn_more_up.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_1.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_2.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_3.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_4.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_5.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_7.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_h_1.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_h_2.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_h_3.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_h_4.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_h_5.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_cancel_h_7.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_ok.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_ok_1.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_ok_2.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_ok_3.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_ok_4.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_ok_5.imageset/ │ │ │ └── Contents.json │ │ ├── btn_msgbox_ok_7.imageset/ │ │ │ └── Contents.json │ │ ├── btn_phoneCode.imageset/ │ │ │ └── Contents.json │ │ ├── btn_phoneNext_d.imageset/ │ │ │ └── Contents.json │ │ ├── btn_phoneNext_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_phoneNext_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_post_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_post_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_refresh_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_refresh_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_select_listened_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_select_listened_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_share_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_share_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_sixin.imageset/ │ │ │ └── Contents.json │ │ ├── btn_sixin_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_skip.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_phone_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_phone_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_qq_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_qq_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_reg_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_reg_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_rr_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_rr_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_sina_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_sina_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_wechat_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_startView_wechat_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_tyq_enter.imageset/ │ │ │ └── Contents.json │ │ ├── btn_tyq_fb_delete.imageset/ │ │ │ └── Contents.json │ │ ├── btn_tyq_play.imageset/ │ │ │ └── Contents.json │ │ ├── btn_unfollow_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_unfollow_n.imageset/ │ │ │ └── Contents.json │ │ ├── btn_wakeup_info_h.imageset/ │ │ │ └── Contents.json │ │ ├── btn_wakeup_info_n.imageset/ │ │ │ └── Contents.json │ │ ├── btsetting1.imageset/ │ │ │ └── Contents.json │ │ ├── btsetting2.imageset/ │ │ │ └── Contents.json │ │ ├── bugtags.imageset/ │ │ │ └── Contents.json │ │ ├── buyPresent.imageset/ │ │ │ └── Contents.json │ │ ├── cancelConnectBtn.imageset/ │ │ │ └── Contents.json │ │ ├── card_bg.imageset/ │ │ │ └── Contents.json │ │ ├── carlifeLogo.imageset/ │ │ │ └── Contents.json │ │ ├── categoryHot.imageset/ │ │ │ └── Contents.json │ │ ├── categoryNew.imageset/ │ │ │ └── Contents.json │ │ ├── category_rec_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── category_rec_mark.imageset/ │ │ │ └── Contents.json │ │ ├── category_rec_play.imageset/ │ │ │ └── Contents.json │ │ ├── category_rec_play_all.imageset/ │ │ │ └── Contents.json │ │ ├── cell_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── cell_arrow_all.imageset/ │ │ │ └── Contents.json │ │ ├── cell_arrow_all_up.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_bottom_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_bottom_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_c.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_chat_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_chat_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_commentline.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_info_bottom.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_info_top.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_loadletter_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_loadletter_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_middle_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_middle_ipad_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_middle_ipad_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_middle_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_middle_noseg_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_middle_noseg_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_newchat.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noData_1.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noData_2.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noData_3.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noData_4.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noData_5.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noData_6.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noData_7.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_noseg_c.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_top_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_top_ipad_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_top_ipad_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_top_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_top_noseg_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_bg_top_noseg_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_bg_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_bg_me.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_bg_other.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_bg_time.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_sharpCorner_l.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_sharpCorner_l_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_sharpCorner_r.imageset/ │ │ │ └── Contents.json │ │ ├── cell_chat_sharpCorner_r_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_comment_sharpcorner.imageset/ │ │ │ └── Contents.json │ │ ├── cell_contactlist_line.imageset/ │ │ │ └── Contents.json │ │ ├── cell_cover_mask.imageset/ │ │ │ └── Contents.json │ │ ├── cell_delete_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_delete_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_download.imageset/ │ │ │ └── Contents.json │ │ ├── cell_download_bg.imageset/ │ │ │ └── Contents.json │ │ ├── cell_download_loading.imageset/ │ │ │ └── Contents.json │ │ ├── cell_download_lock.imageset/ │ │ │ └── Contents.json │ │ ├── cell_downloaded.imageset/ │ │ │ └── Contents.json │ │ ├── cell_downloading.imageset/ │ │ │ └── Contents.json │ │ ├── cell_downstop.imageset/ │ │ │ └── Contents.json │ │ ├── cell_freshnews_arrow_down.imageset/ │ │ │ └── Contents.json │ │ ├── cell_freshnews_down.imageset/ │ │ │ └── Contents.json │ │ ├── cell_freshnews_up.imageset/ │ │ │ └── Contents.json │ │ ├── cell_moreMenu_h.imageset/ │ │ │ └── Contents.json │ │ ├── cell_moreMenu_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_phone_mark.imageset/ │ │ │ └── Contents.json │ │ ├── cell_sound_disable.imageset/ │ │ │ └── Contents.json │ │ ├── cell_sound_pause_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_sound_play_n.imageset/ │ │ │ └── Contents.json │ │ ├── cell_sound_tip.imageset/ │ │ │ └── Contents.json │ │ ├── cell_writeChat.imageset/ │ │ │ └── Contents.json │ │ ├── cgz_ic_accuracy.imageset/ │ │ │ └── Contents.json │ │ ├── changeAlbum.imageset/ │ │ │ └── Contents.json │ │ ├── changeBind.imageset/ │ │ │ └── Contents.json │ │ ├── channel_change_btn.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_left_n.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_left_norma.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_left_p.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_left_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_right_n.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_right_norma.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_right_p.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_right_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_right_share_n.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_right_share_p.imageset/ │ │ │ └── Contents.json │ │ ├── chat_bg_right_w_n.imageset/ │ │ │ └── Contents.json │ │ ├── chat_btn_image.imageset/ │ │ │ └── Contents.json │ │ ├── chat_icon_setting.imageset/ │ │ │ └── Contents.json │ │ ├── checkmark.imageset/ │ │ │ └── Contents.json │ │ ├── checkmark_n.imageset/ │ │ │ └── Contents.json │ │ ├── clickAdIcon.imageset/ │ │ │ └── Contents.json │ │ ├── clickAdIconImg.imageset/ │ │ │ └── Contents.json │ │ ├── closeBind.imageset/ │ │ │ └── Contents.json │ │ ├── closeBtn.imageset/ │ │ │ └── Contents.json │ │ ├── closeShakeLottoryResult.imageset/ │ │ │ └── Contents.json │ │ ├── cmtDetailDelete.imageset/ │ │ │ └── Contents.json │ │ ├── cmtDetailReport.imageset/ │ │ │ └── Contents.json │ │ ├── cmtNoData.imageset/ │ │ │ └── Contents.json │ │ ├── code_bg.imageset/ │ │ │ └── Contents.json │ │ ├── code_btn_change.imageset/ │ │ │ └── Contents.json │ │ ├── color_clear.imageset/ │ │ │ └── Contents.json │ │ ├── columnShadow.imageset/ │ │ │ └── Contents.json │ │ ├── commentShare_bg.imageset/ │ │ │ └── Contents.json │ │ ├── comment_like.imageset/ │ │ │ └── Contents.json │ │ ├── comment_share_check_mark.imageset/ │ │ │ └── Contents.json │ │ ├── comment_unlike.imageset/ │ │ │ └── Contents.json │ │ ├── connectDevice.imageset/ │ │ │ └── Contents.json │ │ ├── connectFail.imageset/ │ │ │ └── Contents.json │ │ ├── connect_wifi.imageset/ │ │ │ └── Contents.json │ │ ├── connextAgain.imageset/ │ │ │ └── Contents.json │ │ ├── contactlist_header_bg.imageset/ │ │ │ └── Contents.json │ │ ├── content_closeView.imageset/ │ │ │ └── Contents.json │ │ ├── couponCardClose.imageset/ │ │ │ └── Contents.json │ │ ├── coupon_limit_bkg.imageset/ │ │ │ └── Contents.json │ │ ├── coupon_limit_buy.imageset/ │ │ │ └── Contents.json │ │ ├── coupon_limit_line.imageset/ │ │ │ └── Contents.json │ │ ├── coupon_limit_success.imageset/ │ │ │ └── Contents.json │ │ ├── coupon_sel.imageset/ │ │ │ └── Contents.json │ │ ├── coupon_unsel.imageset/ │ │ │ └── Contents.json │ │ ├── couponbg.imageset/ │ │ │ └── Contents.json │ │ ├── coverTip.imageset/ │ │ │ └── Contents.json │ │ ├── cover_recommend.imageset/ │ │ │ └── Contents.json │ │ ├── cycle_arrow_down.imageset/ │ │ │ └── Contents.json │ │ ├── cycle_arrow_up.imageset/ │ │ │ └── Contents.json │ │ ├── d_aini.imageset/ │ │ │ └── Contents.json │ │ ├── d_aoteman.imageset/ │ │ │ └── Contents.json │ │ ├── d_baibai.imageset/ │ │ │ └── Contents.json │ │ ├── d_beishang.imageset/ │ │ │ └── Contents.json │ │ ├── d_bishi.imageset/ │ │ │ └── Contents.json │ │ ├── d_bizui.imageset/ │ │ │ └── Contents.json │ │ ├── d_chanzui.imageset/ │ │ │ └── Contents.json │ │ ├── d_chijing.imageset/ │ │ │ └── Contents.json │ │ ├── d_dahaqi.imageset/ │ │ │ └── Contents.json │ │ ├── d_ding.imageset/ │ │ │ └── Contents.json │ │ ├── d_fennu.imageset/ │ │ │ └── Contents.json │ │ ├── d_ganmao.imageset/ │ │ │ └── Contents.json │ │ ├── d_guzhang.imageset/ │ │ │ └── Contents.json │ │ ├── d_haha.imageset/ │ │ │ └── Contents.json │ │ ├── d_haixiu.imageset/ │ │ │ └── Contents.json │ │ ├── d_han.imageset/ │ │ │ └── Contents.json │ │ ├── d_hehe.imageset/ │ │ │ └── Contents.json │ │ ├── d_heixian.imageset/ │ │ │ └── Contents.json │ │ ├── d_heng.imageset/ │ │ │ └── Contents.json │ │ ├── d_huaxin.imageset/ │ │ │ └── Contents.json │ │ ├── d_keai.imageset/ │ │ │ └── Contents.json │ │ ├── d_kelian.imageset/ │ │ │ └── Contents.json │ │ ├── d_ku.imageset/ │ │ │ └── Contents.json │ │ ├── d_kun.imageset/ │ │ │ └── Contents.json │ │ ├── d_landelini.imageset/ │ │ │ └── Contents.json │ │ ├── d_lei.imageset/ │ │ │ └── Contents.json │ │ ├── d_nanhaier.imageset/ │ │ │ └── Contents.json │ │ ├── d_nu.imageset/ │ │ │ └── Contents.json │ │ ├── d_numa.imageset/ │ │ │ └── Contents.json │ │ ├── d_nvhaier.imageset/ │ │ │ └── Contents.json │ │ ├── d_qian.imageset/ │ │ │ └── Contents.json │ │ ├── d_qinqin.imageset/ │ │ │ └── Contents.json │ │ ├── d_shengbing.imageset/ │ │ │ └── Contents.json │ │ ├── d_shiwang.imageset/ │ │ │ └── Contents.json │ │ ├── d_shuai.imageset/ │ │ │ └── Contents.json │ │ ├── d_shudaizi.imageset/ │ │ │ └── Contents.json │ │ ├── d_shuijiao.imageset/ │ │ │ └── Contents.json │ │ ├── d_sikao.imageset/ │ │ │ └── Contents.json │ │ ├── d_taikaixin.imageset/ │ │ │ └── Contents.json │ │ ├── d_touxiao.imageset/ │ │ │ └── Contents.json │ │ ├── d_tu.imageset/ │ │ │ └── Contents.json │ │ ├── d_tuzi.imageset/ │ │ │ └── Contents.json │ │ ├── d_wabishi.imageset/ │ │ │ └── Contents.json │ │ ├── d_weiqu.imageset/ │ │ │ └── Contents.json │ │ ├── d_xiongmao.imageset/ │ │ │ └── Contents.json │ │ ├── d_xixi.imageset/ │ │ │ └── Contents.json │ │ ├── d_xu.imageset/ │ │ │ └── Contents.json │ │ ├── d_yinxian.imageset/ │ │ │ └── Contents.json │ │ ├── d_yiwen.imageset/ │ │ │ └── Contents.json │ │ ├── d_youhengheng.imageset/ │ │ │ └── Contents.json │ │ ├── d_yun.imageset/ │ │ │ └── Contents.json │ │ ├── d_zhuakuang.imageset/ │ │ │ └── Contents.json │ │ ├── d_zhutou.imageset/ │ │ │ └── Contents.json │ │ ├── d_zuoguilian.imageset/ │ │ │ └── Contents.json │ │ ├── d_zuohengheng.imageset/ │ │ │ └── Contents.json │ │ ├── dailylistening_Batch_download.imageset/ │ │ │ └── Contents.json │ │ ├── dailylistening_cell_download.imageset/ │ │ │ └── Contents.json │ │ ├── dailylistening_icon_Play_all.imageset/ │ │ │ └── Contents.json │ │ ├── dailylistening_icon_album.imageset/ │ │ │ └── Contents.json │ │ ├── dailylistening_icon_length.imageset/ │ │ │ └── Contents.json │ │ ├── dailylistening_img_date.imageset/ │ │ │ └── Contents.json │ │ ├── danmubg.imageset/ │ │ │ └── Contents.json │ │ ├── danmubgself.imageset/ │ │ │ └── Contents.json │ │ ├── dash.imageset/ │ │ │ └── Contents.json │ │ ├── dataicon.imageset/ │ │ │ └── Contents.json │ │ ├── delete.imageset/ │ │ │ └── Contents.json │ │ ├── deleteCell_h.imageset/ │ │ │ └── Contents.json │ │ ├── deleteCell_n.imageset/ │ │ │ └── Contents.json │ │ ├── delete_all_h.imageset/ │ │ │ └── Contents.json │ │ ├── delete_all_n.imageset/ │ │ │ └── Contents.json │ │ ├── deletion_selected.imageset/ │ │ │ └── Contents.json │ │ ├── deletion_unselected.imageset/ │ │ │ └── Contents.json │ │ ├── deviceBackView.imageset/ │ │ │ └── Contents.json │ │ ├── device_dot.imageset/ │ │ │ └── Contents.json │ │ ├── device_more.imageset/ │ │ │ └── Contents.json │ │ ├── device_play.imageset/ │ │ │ └── Contents.json │ │ ├── device_playFast.imageset/ │ │ │ └── Contents.json │ │ ├── device_playLike.imageset/ │ │ │ └── Contents.json │ │ ├── device_playLiked.imageset/ │ │ │ └── Contents.json │ │ ├── device_playPause.imageset/ │ │ │ └── Contents.json │ │ ├── device_playPre.imageset/ │ │ │ └── Contents.json │ │ ├── dis_next_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── dis_previous_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── down_n.imageset/ │ │ │ └── Contents.json │ │ ├── downloadAlbum.imageset/ │ │ │ └── Contents.json │ │ ├── downloadMore.imageset/ │ │ │ └── Contents.json │ │ ├── download_ic_download.imageset/ │ │ │ └── Contents.json │ │ ├── download_ic_pause.imageset/ │ │ │ └── Contents.json │ │ ├── download_ic_waiting.imageset/ │ │ │ └── Contents.json │ │ ├── download_ic_wrong.imageset/ │ │ │ └── Contents.json │ │ ├── download_icon_downloadmore.imageset/ │ │ │ └── Contents.json │ │ ├── downloading.imageset/ │ │ │ └── Contents.json │ │ ├── dynamic_album_new.imageset/ │ │ │ └── Contents.json │ │ ├── dynamic_event.imageset/ │ │ │ └── Contents.json │ │ ├── dynamic_more.imageset/ │ │ │ └── Contents.json │ │ ├── dynamic_recommand.imageset/ │ │ │ └── Contents.json │ │ ├── expiredDateBg.imageset/ │ │ │ └── Contents.json │ │ ├── f_geili.imageset/ │ │ │ └── Contents.json │ │ ├── f_hufen.imageset/ │ │ │ └── Contents.json │ │ ├── f_jiong.imageset/ │ │ │ └── Contents.json │ │ ├── f_meng.imageset/ │ │ │ └── Contents.json │ │ ├── f_shenma.imageset/ │ │ │ └── Contents.json │ │ ├── f_shuai.imageset/ │ │ │ └── Contents.json │ │ ├── f_v5.imageset/ │ │ │ └── Contents.json │ │ ├── f_xi.imageset/ │ │ │ └── Contents.json │ │ ├── f_zhi.imageset/ │ │ │ └── Contents.json │ │ ├── fail.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_ic_gift(w).imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_icon_❤_grey.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_icon_❤_pink.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_icon_❤_white.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_icon_❤_yellow.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_top1.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_top2.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_top3.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_top_g.imageset/ │ │ │ └── Contents.json │ │ ├── fanlist_top_p.imageset/ │ │ │ └── Contents.json │ │ ├── favAlbumCell_new.imageset/ │ │ │ └── Contents.json │ │ ├── favSoundDelete.imageset/ │ │ │ └── Contents.json │ │ ├── favSoundDeleteH.imageset/ │ │ │ └── Contents.json │ │ ├── feedAd_del.imageset/ │ │ │ └── Contents.json │ │ ├── feedAd_h.imageset/ │ │ │ └── Contents.json │ │ ├── feedAd_n.imageset/ │ │ │ └── Contents.json │ │ ├── feedbackAlert_close.imageset/ │ │ │ └── Contents.json │ │ ├── feedbackAlert_content.imageset/ │ │ │ └── Contents.json │ │ ├── feedbackAlert_negative_btn.imageset/ │ │ │ └── Contents.json │ │ ├── feedbackAlert_positive_btn.imageset/ │ │ │ └── Contents.json │ │ ├── feedbackImg.imageset/ │ │ │ └── Contents.json │ │ ├── field_bg.imageset/ │ │ │ └── Contents.json │ │ ├── filter_detail.imageset/ │ │ │ └── Contents.json │ │ ├── findCategory_default.imageset/ │ │ │ └── Contents.json │ │ ├── find_Local_Icon.imageset/ │ │ │ └── Contents.json │ │ ├── find_VIP.imageset/ │ │ │ └── Contents.json │ │ ├── find_ad_bg.imageset/ │ │ │ └── Contents.json │ │ ├── find_album_border.imageset/ │ │ │ └── Contents.json │ │ ├── find_album_fav_n.imageset/ │ │ │ └── Contents.json │ │ ├── find_album_unfav_n.imageset/ │ │ │ └── Contents.json │ │ ├── find_albumcell_cover_bg.imageset/ │ │ │ └── Contents.json │ │ ├── find_albumcell_play.imageset/ │ │ │ └── Contents.json │ │ ├── find_all.imageset/ │ │ │ └── Contents.json │ │ ├── find_baijia.imageset/ │ │ │ └── Contents.json │ │ ├── find_book.imageset/ │ │ │ └── Contents.json │ │ ├── find_bubble_large.imageset/ │ │ │ └── Contents.json │ │ ├── find_bubble_middle.imageset/ │ │ │ └── Contents.json │ │ ├── find_bubble_small.imageset/ │ │ │ └── Contents.json │ │ ├── find_campus.imageset/ │ │ │ └── Contents.json │ │ ├── find_car.imageset/ │ │ │ └── Contents.json │ │ ├── find_category_rank_line.imageset/ │ │ │ └── Contents.json │ │ ├── find_chair.imageset/ │ │ │ └── Contents.json │ │ ├── find_change_batch.imageset/ │ │ │ └── Contents.json │ │ ├── find_change_more.imageset/ │ │ │ └── Contents.json │ │ ├── find_comic.imageset/ │ │ │ └── Contents.json │ │ ├── find_comment_star.imageset/ │ │ │ └── Contents.json │ │ ├── find_cover.imageset/ │ │ │ └── Contents.json │ │ ├── find_culture.imageset/ │ │ │ └── Contents.json │ │ ├── find_dot_h.imageset/ │ │ │ └── Contents.json │ │ ├── find_dot_n.imageset/ │ │ │ └── Contents.json │ │ ├── find_down_rec.imageset/ │ │ │ └── Contents.json │ │ ├── find_emotion.imageset/ │ │ │ └── Contents.json │ │ ├── find_entertainment.imageset/ │ │ │ └── Contents.json │ │ ├── find_finance.imageset/ │ │ │ └── Contents.json │ │ ├── find_game.imageset/ │ │ │ └── Contents.json │ │ ├── find_gotocate.imageset/ │ │ │ └── Contents.json │ │ ├── find_health.imageset/ │ │ │ └── Contents.json │ │ ├── find_hotUser_fans.imageset/ │ │ │ └── Contents.json │ │ ├── find_hotUser_following.imageset/ │ │ │ └── Contents.json │ │ ├── find_hotUser_sounds.imageset/ │ │ │ └── Contents.json │ │ ├── find_hotUser_toFollow.imageset/ │ │ │ └── Contents.json │ │ ├── find_hot_albumcover.imageset/ │ │ │ └── Contents.json │ │ ├── find_hotuser_pause.imageset/ │ │ │ └── Contents.json │ │ ├── find_hotuser_play.imageset/ │ │ │ └── Contents.json │ │ ├── find_it.imageset/ │ │ │ └── Contents.json │ │ ├── find_kid.imageset/ │ │ │ └── Contents.json │ │ ├── find_kind_btn_default.imageset/ │ │ │ └── Contents.json │ │ ├── find_movie.imageset/ │ │ │ └── Contents.json │ │ ├── find_music.imageset/ │ │ │ └── Contents.json │ │ ├── find_news.imageset/ │ │ │ └── Contents.json │ │ ├── find_notReachable.imageset/ │ │ │ └── Contents.json │ │ ├── find_opera.imageset/ │ │ │ └── Contents.json │ │ ├── find_other.imageset/ │ │ │ └── Contents.json │ │ ├── find_person_6.imageset/ │ │ │ └── Contents.json │ │ ├── find_person_cover.imageset/ │ │ │ └── Contents.json │ │ ├── find_pop_bg.imageset/ │ │ │ └── Contents.json │ │ ├── find_pop_btn_h.imageset/ │ │ │ └── Contents.json │ │ ├── find_pop_btn_n.imageset/ │ │ │ └── Contents.json │ │ ├── find_pop_btn_sepline.imageset/ │ │ │ └── Contents.json │ │ ├── find_pop_footer.imageset/ │ │ │ └── Contents.json │ │ ├── find_pop_header.imageset/ │ │ │ └── Contents.json │ │ ├── find_pop_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── find_radio.imageset/ │ │ │ └── Contents.json │ │ ├── find_radio_default.imageset/ │ │ │ └── Contents.json │ │ ├── find_radio_focuse.imageset/ │ │ │ └── Contents.json │ │ ├── find_radio_focuse_sel.imageset/ │ │ │ └── Contents.json │ │ ├── find_radioplay.imageset/ │ │ │ └── Contents.json │ │ ├── find_rec_live.imageset/ │ │ │ └── Contents.json │ │ ├── find_rec_mask.imageset/ │ │ │ └── Contents.json │ │ ├── find_rec_play.imageset/ │ │ │ └── Contents.json │ │ ├── find_rec_report.imageset/ │ │ │ └── Contents.json │ │ ├── find_searchbar.imageset/ │ │ │ └── Contents.json │ │ ├── find_seg.imageset/ │ │ │ └── Contents.json │ │ ├── find_selected.imageset/ │ │ │ └── Contents.json │ │ ├── find_special_default.imageset/ │ │ │ └── Contents.json │ │ ├── find_specialicon.imageset/ │ │ │ └── Contents.json │ │ ├── find_subject_icon.imageset/ │ │ │ └── Contents.json │ │ ├── find_tagicon.imageset/ │ │ │ └── Contents.json │ │ ├── find_track_comment.imageset/ │ │ │ └── Contents.json │ │ ├── find_track_pause.imageset/ │ │ │ └── Contents.json │ │ ├── find_track_play.imageset/ │ │ │ └── Contents.json │ │ ├── find_train.imageset/ │ │ │ └── Contents.json │ │ ├── find_travel.imageset/ │ │ │ └── Contents.json │ │ ├── find_usercover.imageset/ │ │ │ └── Contents.json │ │ ├── findcell_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── findfriends_via_contact.imageset/ │ │ │ └── Contents.json │ │ ├── findfriends_via_qq.imageset/ │ │ │ └── Contents.json │ │ ├── findfriends_via_renren.imageset/ │ │ │ └── Contents.json │ │ ├── findfriends_via_weibo.imageset/ │ │ │ └── Contents.json │ │ ├── findfriends_via_wx.imageset/ │ │ │ └── Contents.json │ │ ├── findsection_logo.imageset/ │ │ │ └── Contents.json │ │ ├── findsection_more.imageset/ │ │ │ └── Contents.json │ │ ├── findsubject_large_bg.imageset/ │ │ │ └── Contents.json │ │ ├── findsubject_mask.imageset/ │ │ │ └── Contents.json │ │ ├── flexible.dataset/ │ │ │ ├── Contents.json │ │ │ └── flexible.js │ │ ├── fm_down.imageset/ │ │ │ └── Contents.json │ │ ├── fm_down_h.imageset/ │ │ │ └── Contents.json │ │ ├── fm_up.imageset/ │ │ │ └── Contents.json │ │ ├── fm_up_h.imageset/ │ │ │ └── Contents.json │ │ ├── foldCmtBG.imageset/ │ │ │ └── Contents.json │ │ ├── foldCmt_anchor.imageset/ │ │ │ └── Contents.json │ │ ├── freeTrafficAlertBg.imageset/ │ │ │ └── Contents.json │ │ ├── freeTrafficAlertBg_Nomal.imageset/ │ │ │ └── Contents.json │ │ ├── freeTrafficAlertClose.imageset/ │ │ │ └── Contents.json │ │ ├── freeTrafficOpenBtn.imageset/ │ │ │ └── Contents.json │ │ ├── free_hight.imageset/ │ │ │ └── Contents.json │ │ ├── free_normal.imageset/ │ │ │ └── Contents.json │ │ ├── freshman_coupon.imageset/ │ │ │ └── Contents.json │ │ ├── futuraLT.dataset/ │ │ │ └── Contents.json │ │ ├── game_banner.imageset/ │ │ │ └── Contents.json │ │ ├── game_logined_logo.imageset/ │ │ │ └── Contents.json │ │ ├── game_qq_login.imageset/ │ │ │ └── Contents.json │ │ ├── game_qq_login_h.imageset/ │ │ │ └── Contents.json │ │ ├── game_wechat_login.imageset/ │ │ │ └── Contents.json │ │ ├── game_wechat_login_h.imageset/ │ │ │ └── Contents.json │ │ ├── game_weibo_login.imageset/ │ │ │ └── Contents.json │ │ ├── game_weibo_login_h.imageset/ │ │ │ └── Contents.json │ │ ├── gdticon.imageset/ │ │ │ └── Contents.json │ │ ├── getLottory.imageset/ │ │ │ └── Contents.json │ │ ├── group_bbs_ic_pic.imageset/ │ │ │ └── Contents.json │ │ ├── group_ic_camera.imageset/ │ │ │ └── Contents.json │ │ ├── group_ic_img.imageset/ │ │ │ └── Contents.json │ │ ├── group_ic_topic.imageset/ │ │ │ └── Contents.json │ │ ├── group_icon_checkbox_n.imageset/ │ │ │ └── Contents.json │ │ ├── group_icon_checkbox_p.imageset/ │ │ │ └── Contents.json │ │ ├── group_icon_setting.imageset/ │ │ │ └── Contents.json │ │ ├── group_img_album(null).imageset/ │ │ │ └── Contents.json │ │ ├── group_img_all.imageset/ │ │ │ └── Contents.json │ │ ├── group_img_null.imageset/ │ │ │ └── Contents.json │ │ ├── group_tag_jp.imageset/ │ │ │ └── Contents.json │ │ ├── group_tag_pay.imageset/ │ │ │ └── Contents.json │ │ ├── group_tag_top.imageset/ │ │ │ └── Contents.json │ │ ├── guessRefresh.imageset/ │ │ │ └── Contents.json │ │ ├── guidemask_broadcast.imageset/ │ │ │ └── Contents.json │ │ ├── guidemask_download.imageset/ │ │ │ └── Contents.json │ │ ├── guidemask_inviteting.imageset/ │ │ │ └── Contents.json │ │ ├── guidemask_shareting.imageset/ │ │ │ └── Contents.json │ │ ├── gushiji_1.imageset/ │ │ │ └── Contents.json │ │ ├── gushiji_2.imageset/ │ │ │ └── Contents.json │ │ ├── gushiji_manage.imageset/ │ │ │ └── Contents.json │ │ ├── h_buyao.imageset/ │ │ │ └── Contents.json │ │ ├── h_good.imageset/ │ │ │ └── Contents.json │ │ ├── h_guolai.imageset/ │ │ │ └── Contents.json │ │ ├── h_haha.imageset/ │ │ │ └── Contents.json │ │ ├── h_lai.imageset/ │ │ │ └── Contents.json │ │ ├── h_ok.imageset/ │ │ │ └── Contents.json │ │ ├── h_quantou.imageset/ │ │ │ └── Contents.json │ │ ├── h_ruo.imageset/ │ │ │ └── Contents.json │ │ ├── h_woshou.imageset/ │ │ │ └── Contents.json │ │ ├── h_ye.imageset/ │ │ │ └── Contents.json │ │ ├── h_zan.imageset/ │ │ │ └── Contents.json │ │ ├── h_zuicha.imageset/ │ │ │ └── Contents.json │ │ ├── halfOff4123.imageset/ │ │ │ └── Contents.json │ │ ├── hardware_load.imageset/ │ │ │ └── Contents.json │ │ ├── hardware_shopping.imageset/ │ │ │ └── Contents.json │ │ ├── header_over.imageset/ │ │ │ └── Contents.json │ │ ├── hearContentIV.imageset/ │ │ │ └── Contents.json │ │ ├── hearDownload.imageset/ │ │ │ └── Contents.json │ │ ├── hearGuideClose.imageset/ │ │ │ └── Contents.json │ │ ├── hearHistory.imageset/ │ │ │ └── Contents.json │ │ ├── hearLiveCount.imageset/ │ │ │ └── Contents.json │ │ ├── hearLiveTag.imageset/ │ │ │ └── Contents.json │ │ ├── hearSubscribe.imageset/ │ │ │ └── Contents.json │ │ ├── hearSubscribed.imageset/ │ │ │ └── Contents.json │ │ ├── hear_download.imageset/ │ │ │ └── Contents.json │ │ ├── hear_favourite.imageset/ │ │ │ └── Contents.json │ │ ├── hear_history.imageset/ │ │ │ └── Contents.json │ │ ├── hear_paid.imageset/ │ │ │ └── Contents.json │ │ ├── hear_sort_n.imageset/ │ │ │ └── Contents.json │ │ ├── hear_sort_s.imageset/ │ │ │ └── Contents.json │ │ ├── hobby_ic_learn.imageset/ │ │ │ └── Contents.json │ │ ├── hobby_ic_life.imageset/ │ │ │ └── Contents.json │ │ ├── hobby_ic_listen.imageset/ │ │ │ └── Contents.json │ │ ├── hobby_pic_boy.imageset/ │ │ │ └── Contents.json │ │ ├── hobby_pic_girl.imageset/ │ │ │ └── Contents.json │ │ ├── home_tips_icon_more.imageset/ │ │ │ └── Contents.json │ │ ├── home_tips_icon_turnoff.imageset/ │ │ │ └── Contents.json │ │ ├── hot.imageset/ │ │ │ └── Contents.json │ │ ├── hoticon.imageset/ │ │ │ └── Contents.json │ │ ├── hover.imageset/ │ │ │ └── Contents.json │ │ ├── hybrid_overlay.imageset/ │ │ │ └── Contents.json │ │ ├── ic_?.imageset/ │ │ │ └── Contents.json │ │ ├── ic_LYP.imageset/ │ │ │ └── Contents.json │ │ ├── ic_achieve.imageset/ │ │ │ └── Contents.json │ │ ├── ic_close.imageset/ │ │ │ └── Contents.json │ │ ├── ic_create_album.imageset/ │ │ │ └── Contents.json │ │ ├── ic_discovery_new.imageset/ │ │ │ └── Contents.json │ │ ├── ic_dmgg_buling_o.imageset/ │ │ │ └── Contents.json │ │ ├── ic_dmgg_img_bg.imageset/ │ │ │ └── Contents.json │ │ ├── ic_dmgg_img_bg_o.imageset/ │ │ │ └── Contents.json │ │ ├── ic_dmgg_star_big.imageset/ │ │ │ └── Contents.json │ │ ├── ic_dmgg_star_big_o.imageset/ │ │ │ └── Contents.json │ │ ├── ic_dmgg_star_small.imageset/ │ │ │ └── Contents.json │ │ ├── ic_dmgg_star_small_o.imageset/ │ │ │ └── Contents.json │ │ ├── ic_fans.imageset/ │ │ │ └── Contents.json │ │ ├── ic_gift_.imageset/ │ │ │ └── Contents.json │ │ ├── ic_group.imageset/ │ │ │ └── Contents.json │ │ ├── ic_live.imageset/ │ │ │ └── Contents.json │ │ ├── ic_massage.imageset/ │ │ │ └── Contents.json │ │ ├── ic_money.imageset/ │ │ │ └── Contents.json │ │ ├── ic_more.imageset/ │ │ │ └── Contents.json │ │ ├── ic_navi_group.imageset/ │ │ │ └── Contents.json │ │ ├── ic_navi_group_new.imageset/ │ │ │ └── Contents.json │ │ ├── ic_plus.imageset/ │ │ │ └── Contents.json │ │ ├── ic_popularize.imageset/ │ │ │ └── Contents.json │ │ ├── ic_rec_o.imageset/ │ │ │ └── Contents.json │ │ ├── ic_rec_w.imageset/ │ │ │ └── Contents.json │ │ ├── ic_setting.imageset/ │ │ │ └── Contents.json │ │ ├── ic_sounds.imageset/ │ │ │ └── Contents.json │ │ ├── ic_triangle_up.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_-report.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_albums.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_comment.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_copy.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_delete.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_edit.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_fb_albums_big.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_fb_pic.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_fb_sound.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_like.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_like_on.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_massage.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_massage_red.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_more.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_next.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_reply.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_save.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_stop.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_unfollow.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_zty.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_zty_phone.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_zty_qq.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_zty_wechat.imageset/ │ │ │ └── Contents.json │ │ ├── ic_tyq_zty_weibo.imageset/ │ │ │ └── Contents.json │ │ ├── ic_up.imageset/ │ │ │ └── Contents.json │ │ ├── ic_upload.imageset/ │ │ │ └── Contents.json │ │ ├── ic_v.imageset/ │ │ │ └── Contents.json │ │ ├── ic_wyzb_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon-120.imageset/ │ │ │ └── Contents.json │ │ ├── icon-180.imageset/ │ │ │ └── Contents.json │ │ ├── icon-72.imageset/ │ │ │ └── Contents.json │ │ ├── icon-76.imageset/ │ │ │ └── Contents.json │ │ ├── icon.imageset/ │ │ │ └── Contents.json │ │ ├── icon_add.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_cancel.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_comment.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_coupon.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_pop_album_device.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_share.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_tick.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_top.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_unfav.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_untop.imageset/ │ │ │ └── Contents.json │ │ ├── icon_as_worn.imageset/ │ │ │ └── Contents.json │ │ ├── icon_back.imageset/ │ │ │ └── Contents.json │ │ ├── icon_back_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_back_highlight.imageset/ │ │ │ └── Contents.json │ │ ├── icon_back_round.imageset/ │ │ │ └── Contents.json │ │ ├── icon_back_round_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_bg.imageset/ │ │ │ └── Contents.json │ │ ├── icon_clock.imageset/ │ │ │ └── Contents.json │ │ ├── icon_clock_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_fabu_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_fabu_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_history_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_history_live.imageset/ │ │ │ └── Contents.json │ │ ├── icon_history_mobile.imageset/ │ │ │ └── Contents.json │ │ ├── icon_history_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_history_pc.imageset/ │ │ │ └── Contents.json │ │ ├── icon_history_radio.imageset/ │ │ │ └── Contents.json │ │ ├── icon_home.imageset/ │ │ │ └── Contents.json │ │ ├── icon_home_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_home_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_indicator_down.imageset/ │ │ │ └── Contents.json │ │ ├── icon_indicator_right.imageset/ │ │ │ └── Contents.json │ │ ├── icon_more.imageset/ │ │ │ └── Contents.json │ │ ├── icon_more_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_more_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_more_round.imageset/ │ │ │ └── Contents.json │ │ ├── icon_more_round_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_myView.imageset/ │ │ │ └── Contents.json │ │ ├── icon_playlist_live.imageset/ │ │ │ └── Contents.json │ │ ├── icon_playlist_playback.imageset/ │ │ │ └── Contents.json │ │ ├── icon_playlist_schedules_0.imageset/ │ │ │ └── Contents.json │ │ ├── icon_playlist_schedules_1.imageset/ │ │ │ └── Contents.json │ │ ├── icon_radio_country.imageset/ │ │ │ └── Contents.json │ │ ├── icon_radio_hide.imageset/ │ │ │ └── Contents.json │ │ ├── icon_radio_internet.imageset/ │ │ │ └── Contents.json │ │ ├── icon_radio_local.imageset/ │ │ │ └── Contents.json │ │ ├── icon_radio_province.imageset/ │ │ │ └── Contents.json │ │ ├── icon_radio_show.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_bgm.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_bgm_add.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_bgm_new.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_track_pause.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_track_play.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_tutorial.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_vol.imageset/ │ │ │ └── Contents.json │ │ ├── icon_rec_volcontrol.imageset/ │ │ │ └── Contents.json │ │ ├── icon_search_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_search_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_setting.imageset/ │ │ │ └── Contents.json │ │ ├── icon_setting_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_setting_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_share.imageset/ │ │ │ └── Contents.json │ │ ├── icon_share_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_share_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_sixin.imageset/ │ │ │ └── Contents.json │ │ ├── icon_sixin_h.imageset/ │ │ │ └── Contents.json │ │ ├── icon_sixin_n.imageset/ │ │ │ └── Contents.json │ │ ├── icon_title_rss.imageset/ │ │ │ └── Contents.json │ │ ├── icon_toolbar_morelive.imageset/ │ │ │ └── Contents.json │ │ ├── icon_tyq_tz_like.imageset/ │ │ │ └── Contents.json │ │ ├── icon_x.imageset/ │ │ │ └── Contents.json │ │ ├── iconfont-yaoyiyao.imageset/ │ │ │ └── Contents.json │ │ ├── image_mylive_nodata.imageset/ │ │ │ └── Contents.json │ │ ├── image_two_close.imageset/ │ │ │ └── Contents.json │ │ ├── image_two_mark.imageset/ │ │ │ └── Contents.json │ │ ├── img_carlife.imageset/ │ │ │ └── Contents.json │ │ ├── img_rec_bgm_offline.imageset/ │ │ │ └── Contents.json │ │ ├── img_rec_bgm_wifi.imageset/ │ │ │ └── Contents.json │ │ ├── img_tyq_cellbg.imageset/ │ │ │ └── Contents.json │ │ ├── img_tyq_here.imageset/ │ │ │ └── Contents.json │ │ ├── img_tyq_nofriend.imageset/ │ │ │ └── Contents.json │ │ ├── individualV0.imageset/ │ │ │ └── Contents.json │ │ ├── individualV1.imageset/ │ │ │ └── Contents.json │ │ ├── individualV10.imageset/ │ │ │ └── Contents.json │ │ ├── individualV11.imageset/ │ │ │ └── Contents.json │ │ ├── individualV12.imageset/ │ │ │ └── Contents.json │ │ ├── individualV13.imageset/ │ │ │ └── Contents.json │ │ ├── individualV14.imageset/ │ │ │ └── Contents.json │ │ ├── individualV15.imageset/ │ │ │ └── Contents.json │ │ ├── individualV16.imageset/ │ │ │ └── Contents.json │ │ ├── individualV2.imageset/ │ │ │ └── Contents.json │ │ ├── individualV3.imageset/ │ │ │ └── Contents.json │ │ ├── individualV4.imageset/ │ │ │ └── Contents.json │ │ ├── individualV5.imageset/ │ │ │ └── Contents.json │ │ ├── individualV6.imageset/ │ │ │ └── Contents.json │ │ ├── individualV7.imageset/ │ │ │ └── Contents.json │ │ ├── individualV8.imageset/ │ │ │ └── Contents.json │ │ ├── individualV9.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV0.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV1.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV10.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV11.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV12.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV13.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV14.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV15.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV16.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV2.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV3.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV4.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV5.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV6.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV7.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV8.imageset/ │ │ │ └── Contents.json │ │ ├── individual_blueV9.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV0.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV014.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV1.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV10.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV11.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV12.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV13.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV15.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV16.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV2.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV3.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV4.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV5.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV6.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV7.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV8.imageset/ │ │ │ └── Contents.json │ │ ├── individual_orangeV9.imageset/ │ │ │ └── Contents.json │ │ ├── input_h.imageset/ │ │ │ └── Contents.json │ │ ├── input_n.imageset/ │ │ │ └── Contents.json │ │ ├── iocn_Screening_h.imageset/ │ │ │ └── Contents.json │ │ ├── iocn_Screening_n.imageset/ │ │ │ └── Contents.json │ │ ├── jmgl_ic_college.imageset/ │ │ │ └── Contents.json │ │ ├── join_vip_icon.imageset/ │ │ │ └── Contents.json │ │ ├── jshandle.dataset/ │ │ │ ├── Contents.json │ │ │ └── jshandle.js │ │ ├── l_aixinchuandi.imageset/ │ │ │ └── Contents.json │ │ ├── l_shangxin.imageset/ │ │ │ └── Contents.json │ │ ├── l_xin.imageset/ │ │ │ └── Contents.json │ │ ├── lastLoginTip.imageset/ │ │ │ └── Contents.json │ │ ├── leftside_shadow_bg.imageset/ │ │ │ └── Contents.json │ │ ├── lineShadow.imageset/ │ │ │ └── Contents.json │ │ ├── line_horizeontal.imageset/ │ │ │ └── Contents.json │ │ ├── liveClose.imageset/ │ │ │ └── Contents.json │ │ ├── livePlayingHistory.imageset/ │ │ │ └── Contents.json │ │ ├── livePlayingList.imageset/ │ │ │ └── Contents.json │ │ ├── livePlayingStatusLive.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioCellPause.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioCellPlay.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioCellPoint.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioDropDownButtonBg.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioPlay_album_mask.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioPlayingBack.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioProvinceBg_High.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioProvinceBg_Normal.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioSectionMore_High.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadioSectionMore_Normal.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadio_activity_moreLive.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadio_album_mask480.imageset/ │ │ │ └── Contents.json │ │ ├── liveRadio_beta.imageset/ │ │ │ └── Contents.json │ │ ├── liveRaido_album_mask.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_chat_other.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_close.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_close2.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_close_white.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_count.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_default.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_gift.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_gridmask.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_imgmask.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_mask.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_tips.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_tips1.imageset/ │ │ │ └── Contents.json │ │ ├── live_bg_triangle.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_addmusic.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_admin_add.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_admin_remove.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_adminlist.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_call.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_call_online.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_call_online_00.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_call_online_01.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_call_online_03.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_checked.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_close.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_comment.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_confirm.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_delete.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_emoji.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_end.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_followe.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_followed.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_fullscreen.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_fullscreen_exit.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_gallery.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_gift.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_hangup.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_hide.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_hit.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_call.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_call_online.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_comment.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_manage.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_menu.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_micoff.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_micon.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_mixer.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_music.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_photo.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_sound.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_host_topic.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_image.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_infohide.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_keyboard.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_manage.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_menu.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_mic.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_micoff.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_micon.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_mixer.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_add.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_bgm.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_download.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_loop.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_pause.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_play.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_remove.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_repeat.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_music_vol.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_new.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_newmessage.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_next.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_notice.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_pause.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_photo.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_play.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_prev.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_replay.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_rotate.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_saveImage.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_setting.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_share.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_slide_hide.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_slide_show.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_group_off.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_group_on.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_moment_off.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_moment_on.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_qq_off.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_qq_on.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_qzone_off.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_qzone_on.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_tyq_off.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_tyq_on.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_wechat_off.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_wechat_on.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_weibo_off.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_social_weibo_on.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_sound.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_speaker.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_topic.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_unchecked.imageset/ │ │ │ └── Contents.json │ │ ├── live_btn_vol.imageset/ │ │ │ └── Contents.json │ │ ├── live_gift_bg_pop.imageset/ │ │ │ └── Contents.json │ │ ├── live_gift_icon_down.imageset/ │ │ │ └── Contents.json │ │ ├── live_gift_icon_right.imageset/ │ │ │ └── Contents.json │ │ ├── live_gift_icon_selected.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_anchor.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_arrow_right.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_badge_top1.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_badge_top2.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_badge_top3.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_count.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_date.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_defaultgift.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_diamond.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_follow.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_gif_play.dataset/ │ │ │ ├── Contents.json │ │ │ └── live_ic_gif_play.json │ │ ├── live_ic_infohide.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_infoshow.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_music_added.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_music_cloud.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_music_palying.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_music_pc.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_mute.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_position.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_profile_time.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_ranking_down.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_ranking_keep.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_ranking_new.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_ranking_top1.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_ranking_top2.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_ranking_top3.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_ranking_up.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sex_female.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sex_male.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sex_unknown.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_awkward.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_crow.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_handclap.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_laugh.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_love.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_nice.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_thumbdown.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_sound_train.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_total.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_v.imageset/ │ │ │ └── Contents.json │ │ ├── live_ic_warning.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_anchor.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_avatar_ting.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_cover.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_default.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_end_list.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_home_status_onliving.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_home_topanchor.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_honor_highlight.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_line.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_live_list.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_loading.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_loading2.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_nav_default.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_no_chatgroup.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_offline.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_redpacket.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_redpacket_send.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_replay_list.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_right1.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_right2.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_right3.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_status_onair.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_status_onair_red.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_status_soon.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_status_trailer.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_streamend.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_texture.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_texture_line.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_tips_addmusic.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_tips_group.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_tips_liveinfo.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_tips_music.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_trailer_list.imageset/ │ │ │ └── Contents.json │ │ ├── live_img_wealth_basic.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_admin.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_adminlist.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_delete.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_end.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_feedback.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_headphone.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_info.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_livelist.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_mute.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_notice.imageset/ │ │ │ └── Contents.json │ │ ├── live_menu_share.imageset/ │ │ │ └── Contents.json │ │ ├── live_radio_playing_bg.imageset/ │ │ │ └── Contents.json │ │ ├── local_play_icon.imageset/ │ │ │ └── Contents.json │ │ ├── log.imageset/ │ │ │ └── Contents.json │ │ ├── logicon.imageset/ │ │ │ └── Contents.json │ │ ├── login_bg.imageset/ │ │ │ └── Contents.json │ │ ├── login_img_door.imageset/ │ │ │ └── Contents.json │ │ ├── login_or.imageset/ │ │ │ └── Contents.json │ │ ├── logo_laya.imageset/ │ │ │ └── Contents.json │ │ ├── logo_recommend.imageset/ │ │ │ └── Contents.json │ │ ├── logo_xima.imageset/ │ │ │ └── Contents.json │ │ ├── lostLottory.imageset/ │ │ │ └── Contents.json │ │ ├── lottoryBgBlue.imageset/ │ │ │ └── Contents.json │ │ ├── lottoryBgRed.imageset/ │ │ │ └── Contents.json │ │ ├── lottoryBgWhite.imageset/ │ │ │ └── Contents.json │ │ ├── lottoryBgYellow.imageset/ │ │ │ └── Contents.json │ │ ├── lottoryExpired.imageset/ │ │ │ └── Contents.json │ │ ├── lottoryIcon.imageset/ │ │ │ └── Contents.json │ │ ├── luna_wps.imageset/ │ │ │ └── Contents.json │ │ ├── mainLogo.imageset/ │ │ │ └── Contents.json │ │ ├── mainLogo_MINI.imageset/ │ │ │ └── Contents.json │ │ ├── mark_title.imageset/ │ │ │ └── Contents.json │ │ ├── me.imageset/ │ │ │ └── Contents.json │ │ ├── meArrow.imageset/ │ │ │ └── Contents.json │ │ ├── meBg.imageset/ │ │ │ └── Contents.json │ │ ├── meBought.imageset/ │ │ │ └── Contents.json │ │ ├── meLine.imageset/ │ │ │ └── Contents.json │ │ ├── meMesHL.imageset/ │ │ │ └── Contents.json │ │ ├── meMesNor.imageset/ │ │ │ └── Contents.json │ │ ├── meRecord.imageset/ │ │ │ └── Contents.json │ │ ├── meSetHL.imageset/ │ │ │ └── Contents.json │ │ ├── meSetNor.imageset/ │ │ │ └── Contents.json │ │ ├── meSoundEdit_topView_browser_default.imageset/ │ │ │ └── Contents.json │ │ ├── meSoundEdit_topView_default.imageset/ │ │ │ └── Contents.json │ │ ├── meTicket.imageset/ │ │ │ └── Contents.json │ │ ├── meWallet.imageset/ │ │ │ └── Contents.json │ │ ├── meXB.imageset/ │ │ │ └── Contents.json │ │ ├── me_album_delete.imageset/ │ │ │ └── Contents.json │ │ ├── me_album_edit.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_email.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_email_h.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_phone.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_phone_h.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_qq.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_qq_h.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_renren.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_renren_h.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_sina.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_sina_h.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_wechat.imageset/ │ │ │ └── Contents.json │ │ ├── me_celllogo_wechat_h.imageset/ │ │ │ └── Contents.json │ │ ├── me_fans_verifylogo.imageset/ │ │ │ └── Contents.json │ │ ├── me_more_delete.imageset/ │ │ │ └── Contents.json │ │ ├── me_more_download.imageset/ │ │ │ └── Contents.json │ │ ├── me_more_edit.imageset/ │ │ │ └── Contents.json │ │ ├── me_more_replace.imageset/ │ │ │ └── Contents.json │ │ ├── me_my_device.imageset/ │ │ │ └── Contents.json │ │ ├── me_new_sound.imageset/ │ │ │ └── Contents.json │ │ ├── me_relate_bg.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_CPS.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_account.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_achievement.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_anchor.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_beAnchor.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_boughttracks.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_clock.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_college.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_comment.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_contact.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_coupon.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_device.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_favAlbum.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_feedback.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_findting.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_game.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_identify.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_liked.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_more.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_myorder.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_nightmode.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_notice_center.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_playhis.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_plus.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_points.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_program.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_refunds.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_setting.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_sixin.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_social.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_store.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_union.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_usercenter.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_zone.imageset/ │ │ │ └── Contents.json │ │ ├── me_setting_zonemsg.imageset/ │ │ │ └── Contents.json │ │ ├── me_unbindPhone.imageset/ │ │ │ └── Contents.json │ │ ├── messageCenter_icon_sixin_n.imageset/ │ │ │ └── Contents.json │ │ ├── message_icon_setting.imageset/ │ │ │ └── Contents.json │ │ ├── message_icon_silence.imageset/ │ │ │ └── Contents.json │ │ ├── message_no_history.imageset/ │ │ │ └── Contents.json │ │ ├── metal_insert.imageset/ │ │ │ └── Contents.json │ │ ├── mini_lock_line.imageset/ │ │ │ └── Contents.json │ │ ├── mini_lockbg.imageset/ │ │ │ └── Contents.json │ │ ├── mini_skip_normal.imageset/ │ │ │ └── Contents.json │ │ ├── mini_skip_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── moreBriefBtn_n.imageset/ │ │ │ └── Contents.json │ │ ├── moreBriefBtn_s.imageset/ │ │ │ └── Contents.json │ │ ├── moreBrief_triangle.imageset/ │ │ │ └── Contents.json │ │ ├── moreBtnFree.imageset/ │ │ │ └── Contents.json │ │ ├── moreBtnMask.imageset/ │ │ │ └── Contents.json │ │ ├── moreBtnPay.imageset/ │ │ │ └── Contents.json │ │ ├── moreViewPrivateTalk.imageset/ │ │ │ └── Contents.json │ │ ├── moreViewReplay.imageset/ │ │ │ └── Contents.json │ │ ├── moreViewTop.imageset/ │ │ │ └── Contents.json │ │ ├── msgAuditInreview.imageset/ │ │ │ └── Contents.json │ │ ├── msgAuditReject.imageset/ │ │ │ └── Contents.json │ │ ├── msuicheting.imageset/ │ │ │ └── Contents.json │ │ ├── musicPersonImage.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterFilterNormal.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterFilterSelected.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterNoAlbum.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterNoCmt.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterPayAlbumComment.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterQA.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterSortIconNormal.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterSortIconSelected.imageset/ │ │ │ └── Contents.json │ │ ├── myCenterSortSelected.imageset/ │ │ │ └── Contents.json │ │ ├── my_bg_wdl.imageset/ │ │ │ └── Contents.json │ │ ├── my_ic_feedback.imageset/ │ │ │ └── Contents.json │ │ ├── my_ic_more.imageset/ │ │ │ └── Contents.json │ │ ├── navidrop_arrow_down_h.imageset/ │ │ │ └── Contents.json │ │ ├── navidrop_arrow_down_n.imageset/ │ │ │ └── Contents.json │ │ ├── navidrop_arrow_up_h.imageset/ │ │ │ └── Contents.json │ │ ├── navidrop_arrow_up_n.imageset/ │ │ │ └── Contents.json │ │ ├── navidrop_h.imageset/ │ │ │ └── Contents.json │ │ ├── navigation_line.imageset/ │ │ │ └── Contents.json │ │ ├── navigationbar_bg.imageset/ │ │ │ └── Contents.json │ │ ├── navigationbar_bg_64.imageset/ │ │ │ └── Contents.json │ │ ├── networkhelp.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_AD_cover_bg.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_AD_split.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_VIP_mark.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_comment_btn.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_commet_defualt_user.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_cover_free.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_cover_free_320.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_cover_payment.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_cover_payment_320.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_empt_star.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_end.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_free_downloadw.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_full_star.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_half_star.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_intro_back.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_lastplay_icon.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_pay_rss.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_pay_rss_bg.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_pay_rssed.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_pay_rssed_bg.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_pay_shop.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_pay_shop_bg.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_scroll_bottom.imageset/ │ │ │ └── Contents.json │ │ ├── newAlbum_scroll_bottom_hight.imageset/ │ │ │ └── Contents.json │ │ ├── newUserMask.imageset/ │ │ │ └── Contents.json │ │ ├── new_album_playCount.imageset/ │ │ │ └── Contents.json │ │ ├── nextStep.imageset/ │ │ │ └── Contents.json │ │ ├── next_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── noCopyright.imageset/ │ │ │ └── Contents.json │ │ ├── noDataCell.imageset/ │ │ │ └── Contents.json │ │ ├── noData_VIP.imageset/ │ │ │ └── Contents.json │ │ ├── noData_consume.imageset/ │ │ │ └── Contents.json │ │ ├── noData_freeting.imageset/ │ │ │ └── Contents.json │ │ ├── noData_recharge.imageset/ │ │ │ └── Contents.json │ │ ├── noData_subscription2.imageset/ │ │ │ └── Contents.json │ │ ├── noData_zone_receive.imageset/ │ │ │ └── Contents.json │ │ ├── noMoreCmt.imageset/ │ │ │ └── Contents.json │ │ ├── noSearchData.imageset/ │ │ │ └── Contents.json │ │ ├── no_device.imageset/ │ │ │ └── Contents.json │ │ ├── no_network.imageset/ │ │ │ └── Contents.json │ │ ├── noread_icon.imageset/ │ │ │ └── Contents.json │ │ ├── notShowPwd.imageset/ │ │ │ └── Contents.json │ │ ├── now_playing.imageset/ │ │ │ └── Contents.json │ │ ├── npCoverMask.imageset/ │ │ │ └── Contents.json │ │ ├── npTabSponsor.imageset/ │ │ │ └── Contents.json │ │ ├── np_QACmt.imageset/ │ │ │ └── Contents.json │ │ ├── np_artistheader_bg.imageset/ │ │ │ └── Contents.json │ │ ├── np_comment_btn.imageset/ │ │ │ └── Contents.json │ │ ├── np_commentsummary_bg.imageset/ │ │ │ └── Contents.json │ │ ├── np_danmu_bg.imageset/ │ │ │ └── Contents.json │ │ ├── np_danmu_logo.imageset/ │ │ │ └── Contents.json │ │ ├── np_emoticon_delete_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_face_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_face_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_headview_bg.imageset/ │ │ │ └── Contents.json │ │ ├── np_headview_bg_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_headview_following_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_headview_nofollow_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_image_complete.imageset/ │ │ │ └── Contents.json │ │ ├── np_image_origin.imageset/ │ │ │ └── Contents.json │ │ ├── np_image_save.imageset/ │ │ │ └── Contents.json │ │ ├── np_image_share.imageset/ │ │ │ └── Contents.json │ │ ├── np_input_back_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_input_back_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_input_danmu.imageset/ │ │ │ └── Contents.json │ │ ├── np_input_keyboard_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_input_keyboard_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_input_logo.imageset/ │ │ │ └── Contents.json │ │ ├── np_input_text_bg.imageset/ │ │ │ └── Contents.json │ │ ├── np_like_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_like_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_line.imageset/ │ │ │ └── Contents.json │ │ ├── np_loading.imageset/ │ │ │ └── Contents.json │ │ ├── np_loading_failed.imageset/ │ │ │ └── Contents.json │ │ ├── np_loading_fresh.imageset/ │ │ │ └── Contents.json │ │ ├── np_logo.imageset/ │ │ │ └── Contents.json │ │ ├── np_lover_mark.imageset/ │ │ │ └── Contents.json │ │ ├── np_loverheader_bg.imageset/ │ │ │ └── Contents.json │ │ ├── np_lovers_count_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_lovers_count_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── np_loverview_arraw.imageset/ │ │ │ └── Contents.json │ │ ├── np_menu_playlist.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_album.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_down.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_history.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_public.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_replay.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_report.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_share.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_sleep.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_wakeup.imageset/ │ │ │ └── Contents.json │ │ ├── np_more_wifi.imageset/ │ │ │ └── Contents.json │ │ ├── np_pageicon3_1.imageset/ │ │ │ └── Contents.json │ │ ├── np_pageicon3_2.imageset/ │ │ │ └── Contents.json │ │ ├── np_pageicon3_3.imageset/ │ │ │ └── Contents.json │ │ ├── np_pageicon_1.imageset/ │ │ │ └── Contents.json │ │ ├── np_pageicon_2.imageset/ │ │ │ └── Contents.json │ │ ├── np_pic_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── np_picmask.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_cycle.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_order.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_outorder.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_reverse_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_single.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_sort_def.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_sort_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_sound_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_playlist_sound_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_pop_ds.imageset/ │ │ │ └── Contents.json │ │ ├── np_pop_home.imageset/ │ │ │ └── Contents.json │ │ ├── np_pop_sixin.imageset/ │ │ │ └── Contents.json │ │ ├── np_pop_tiwen.imageset/ │ │ │ └── Contents.json │ │ ├── np_sound_playtimes.imageset/ │ │ │ └── Contents.json │ │ ├── np_sound_playtimes_b.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_back_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_back_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_bg.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_chatGroup.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_clock_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_clock_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_cm_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_comment.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_comment_none.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_dm_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_download_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_download_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_gift.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_like_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_like_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_likenum_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_likenum_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_likenum_nv.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_more_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_red.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_share_h.imageset/ │ │ │ └── Contents.json │ │ ├── np_toolbar_share_n.imageset/ │ │ │ └── Contents.json │ │ ├── np_user_more.imageset/ │ │ │ └── Contents.json │ │ ├── o_bingun.imageset/ │ │ │ └── Contents.json │ │ ├── o_dangao.imageset/ │ │ │ └── Contents.json │ │ ├── o_dianying.imageset/ │ │ │ └── Contents.json │ │ ├── o_fahongbao.imageset/ │ │ │ └── Contents.json │ │ ├── o_feiji.imageset/ │ │ │ └── Contents.json │ │ ├── o_fengshan.imageset/ │ │ │ └── Contents.json │ │ ├── o_ganbei.imageset/ │ │ │ └── Contents.json │ │ ├── o_hongsidai.imageset/ │ │ │ └── Contents.json │ │ ├── o_huatong.imageset/ │ │ │ └── Contents.json │ │ ├── o_kafei.imageset/ │ │ │ └── Contents.json │ │ ├── o_lazhu.imageset/ │ │ │ └── Contents.json │ │ ├── o_liwu.imageset/ │ │ │ └── Contents.json │ │ ├── o_lvsidai.imageset/ │ │ │ └── Contents.json │ │ ├── o_qiche.imageset/ │ │ │ └── Contents.json │ │ ├── o_shixi.imageset/ │ │ │ └── Contents.json │ │ ├── o_shouji.imageset/ │ │ │ └── Contents.json │ │ ├── o_shoutao.imageset/ │ │ │ └── Contents.json │ │ ├── o_weibo.imageset/ │ │ │ └── Contents.json │ │ ├── o_weiguan.imageset/ │ │ │ └── Contents.json │ │ ├── o_wennuanmaozi.imageset/ │ │ │ └── Contents.json │ │ ├── o_xigua.imageset/ │ │ │ └── Contents.json │ │ ├── o_yinyue.imageset/ │ │ │ └── Contents.json │ │ ├── o_zhaoxiangji.imageset/ │ │ │ └── Contents.json │ │ ├── o_zhong.imageset/ │ │ │ └── Contents.json │ │ ├── o_zixingche.imageset/ │ │ │ └── Contents.json │ │ ├── o_zuqiu.imageset/ │ │ │ └── Contents.json │ │ ├── oauth_check.imageset/ │ │ │ └── Contents.json │ │ ├── oauth_switch.imageset/ │ │ │ └── Contents.json │ │ ├── oauth_uncheck.imageset/ │ │ │ └── Contents.json │ │ ├── other_info_bg.imageset/ │ │ │ └── Contents.json │ │ ├── other_info_close.imageset/ │ │ │ └── Contents.json │ │ ├── paid_rec_activity_1.imageset/ │ │ │ └── Contents.json │ │ ├── paid_rec_activity_2.imageset/ │ │ │ └── Contents.json │ │ ├── paid_rec_album_border.imageset/ │ │ │ └── Contents.json │ │ ├── paid_rec_album_cover.imageset/ │ │ │ └── Contents.json │ │ ├── paid_rec_album_high.imageset/ │ │ │ └── Contents.json │ │ ├── paid_rec_album_rate.imageset/ │ │ │ └── Contents.json │ │ ├── paused.imageset/ │ │ │ └── Contents.json │ │ ├── pay_album_bottom.imageset/ │ │ │ └── Contents.json │ │ ├── pay_album_left.imageset/ │ │ │ └── Contents.json │ │ ├── pay_album_right.imageset/ │ │ │ └── Contents.json │ │ ├── pay_albumheader_bg.imageset/ │ │ │ └── Contents.json │ │ ├── pay_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── pay_arrow_ic.imageset/ │ │ │ └── Contents.json │ │ ├── pay_arrow_up.imageset/ │ │ │ └── Contents.json │ │ ├── pay_audition.imageset/ │ │ │ └── Contents.json │ │ ├── pay_banner.imageset/ │ │ │ └── Contents.json │ │ ├── pay_box.imageset/ │ │ │ └── Contents.json │ │ ├── pay_boxselect.imageset/ │ │ │ └── Contents.json │ │ ├── pay_btn_top.imageset/ │ │ │ └── Contents.json │ │ ├── pay_btn_unfold.imageset/ │ │ │ └── Contents.json │ │ ├── pay_bubble_ad_adorn.imageset/ │ │ │ └── Contents.json │ │ ├── pay_bubble_ad_bg.imageset/ │ │ │ └── Contents.json │ │ ├── pay_button_bkg.imageset/ │ │ │ └── Contents.json │ │ ├── pay_close.imageset/ │ │ │ └── Contents.json │ │ ├── pay_coupon.imageset/ │ │ │ └── Contents.json │ │ ├── pay_coupon_empty.imageset/ │ │ │ └── Contents.json │ │ ├── pay_coupon_more.imageset/ │ │ │ └── Contents.json │ │ ├── pay_coupon_nor.imageset/ │ │ │ └── Contents.json │ │ ├── pay_cown.imageset/ │ │ │ └── Contents.json │ │ ├── pay_device_btnbg.imageset/ │ │ │ └── Contents.json │ │ ├── pay_dialoge.imageset/ │ │ │ └── Contents.json │ │ ├── pay_downloadfail.imageset/ │ │ │ └── Contents.json │ │ ├── pay_explain.imageset/ │ │ │ └── Contents.json │ │ ├── pay_fail.imageset/ │ │ │ └── Contents.json │ │ ├── pay_fold_more_bg.imageset/ │ │ │ └── Contents.json │ │ ├── pay_gift_tips.imageset/ │ │ │ └── Contents.json │ │ ├── pay_giftarrow.imageset/ │ │ │ └── Contents.json │ │ ├── pay_iap_finish.imageset/ │ │ │ └── Contents.json │ │ ├── pay_iap_refresh.imageset/ │ │ │ └── Contents.json │ │ ├── pay_img_line.imageset/ │ │ │ └── Contents.json │ │ ├── pay_intro.imageset/ │ │ │ └── Contents.json │ │ ├── pay_ipa_tooth.imageset/ │ │ │ └── Contents.json │ │ ├── pay_loading.imageset/ │ │ │ └── Contents.json │ │ ├── pay_mask_image.imageset/ │ │ │ └── Contents.json │ │ ├── pay_nocomment.imageset/ │ │ │ └── Contents.json │ │ ├── pay_nofreetrack.imageset/ │ │ │ └── Contents.json │ │ ├── pay_nonewtrack.imageset/ │ │ │ └── Contents.json │ │ ├── pay_outline_sound.imageset/ │ │ │ └── Contents.json │ │ ├── pay_outline_time.imageset/ │ │ │ └── Contents.json │ │ ├── pay_play_icon_p1.imageset/ │ │ │ └── Contents.json │ │ ├── pay_play_icon_p2.imageset/ │ │ │ └── Contents.json │ │ ├── pay_play_icon_p3.imageset/ │ │ │ └── Contents.json │ │ ├── pay_red_icon.imageset/ │ │ │ └── Contents.json │ │ ├── pay_redgift_empty.imageset/ │ │ │ └── Contents.json │ │ ├── pay_redgift_share_icon.imageset/ │ │ │ └── Contents.json │ │ ├── pay_refund.imageset/ │ │ │ └── Contents.json │ │ ├── pay_refund_nor.imageset/ │ │ │ └── Contents.json │ │ ├── pay_restore.imageset/ │ │ │ └── Contents.json │ │ ├── pay_state_bg.imageset/ │ │ │ └── Contents.json │ │ ├── pay_success.imageset/ │ │ │ └── Contents.json │ │ ├── pay_success_big.imageset/ │ │ │ └── Contents.json │ │ ├── pay_tracksample.imageset/ │ │ │ └── Contents.json │ │ ├── pay_trailer.imageset/ │ │ │ └── Contents.json │ │ ├── pay_unfolded_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── pay_uptop.imageset/ │ │ │ └── Contents.json │ │ ├── pay_vip.imageset/ │ │ │ └── Contents.json │ │ ├── pay_vip_nor.imageset/ │ │ │ └── Contents.json │ │ ├── pay_warning.imageset/ │ │ │ └── Contents.json │ │ ├── pay_wechart.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_addimage.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_allstar.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_checkinfo.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_delcmt.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_halfstar.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_host.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_hosttriangle.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_like.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_liked.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_more.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_moreimg.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_qq.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_reply.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_score_default.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_smile.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_star.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_star_default.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_star_light.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_success.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_unhappy.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_wechat.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_wechatf.imageset/ │ │ │ └── Contents.json │ │ ├── paycmt_write.imageset/ │ │ │ └── Contents.json │ │ ├── payment_mark_select.imageset/ │ │ │ └── Contents.json │ │ ├── payment_mark_unselect.imageset/ │ │ │ └── Contents.json │ │ ├── persentQQ.imageset/ │ │ │ └── Contents.json │ │ ├── persentWechat.imageset/ │ │ │ └── Contents.json │ │ ├── personal_baiyang_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_baiyang_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_chunv_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_chunv_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_female_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_female_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_jinniu_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_jinniu_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_juxie_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_juxie_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_male_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_male_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_mojie_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_mojie_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_region_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_region_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_sheshou_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_sheshou_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shizi_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shizi_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shuangyu_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shuangyu_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shuangzi_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shuangzi_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shuiping_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_shuiping_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_tiancheng_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_tiancheng_w.imageset/ │ │ │ └── Contents.json │ │ ├── personal_tianxie_b.imageset/ │ │ │ └── Contents.json │ │ ├── personal_tianxie_w.imageset/ │ │ │ └── Contents.json │ │ ├── placeHolder.imageset/ │ │ │ └── Contents.json │ │ ├── playCoverBtn_n.imageset/ │ │ │ └── Contents.json │ │ ├── playCoverBtn_s.imageset/ │ │ │ └── Contents.json │ │ ├── playProcessBar.imageset/ │ │ │ └── Contents.json │ │ ├── playProcessBg.imageset/ │ │ │ └── Contents.json │ │ ├── playProcessCe.imageset/ │ │ │ └── Contents.json │ │ ├── playProcessDot_n.imageset/ │ │ │ └── Contents.json │ │ ├── playProcessPc.imageset/ │ │ │ └── Contents.json │ │ ├── playTimeBg_h.imageset/ │ │ │ └── Contents.json │ │ ├── playTimeBg_n.imageset/ │ │ │ └── Contents.json │ │ ├── play_album_fav_n.imageset/ │ │ │ └── Contents.json │ │ ├── play_album_unfav_n.imageset/ │ │ │ └── Contents.json │ │ ├── play_fast_back.imageset/ │ │ │ └── Contents.json │ │ ├── play_fast_forword.imageset/ │ │ │ └── Contents.json │ │ ├── play_flag_wave_0.imageset/ │ │ │ └── Contents.json │ │ ├── play_flag_wave_1.imageset/ │ │ │ └── Contents.json │ │ ├── play_flag_wave_2.imageset/ │ │ │ └── Contents.json │ │ ├── play_removead_ic_before.imageset/ │ │ │ └── Contents.json │ │ ├── player_btn_comment.imageset/ │ │ │ └── Contents.json │ │ ├── player_btn_fullsize.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_bg_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_icon_indicator.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_icon_x.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_img_head.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_img_light.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_text_top1.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_text_top2.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_text_top3.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_text_zzcg.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top1_bg_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top1_img_head.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top1_img_light.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top2_bg_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top2_img_head.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top2_img_light.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top3_bg_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top3_img_head.imageset/ │ │ │ └── Contents.json │ │ ├── player_popup_top3_img_light.imageset/ │ │ │ └── Contents.json │ │ ├── player_vipad_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── playing_tips_finger.imageset/ │ │ │ └── Contents.json │ │ ├── playing_tips_text.imageset/ │ │ │ └── Contents.json │ │ ├── playingalbum_fav.imageset/ │ │ │ └── Contents.json │ │ ├── playingalbum_fav_n.imageset/ │ │ │ └── Contents.json │ │ ├── playingback.imageset/ │ │ │ └── Contents.json │ │ ├── playingback_h.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_bg_pop.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_bg_title.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_+15s_normal.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_+15s_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_-15s_normal.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_-15s_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_2x_normal.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_2x_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_barrage_normal.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_barrage_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_comment_normal.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_comment_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_down_black.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_down_white.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_down_white_HL.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_download_normal.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_download_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_dynamic_rhythm_p1.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_dynamic_rhythm_p2.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_dynamic_rhythm_p3.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_like_normal.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_like_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_list.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_more_black.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_more_vertical.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_more_white.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_more_white_HL.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_play.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_share_black.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_share_white.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_share_white_HL.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_soundInfo_black.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_soundInfo_white.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_soundVideo_black.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_soundVideo_white.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_suspend.imageset/ │ │ │ └── Contents.json │ │ ├── playpage_icon_timing.imageset/ │ │ │ └── Contents.json │ │ ├── plu_fav.imageset/ │ │ │ └── Contents.json │ │ ├── plu_header.imageset/ │ │ │ └── Contents.json │ │ ├── plu_logo.imageset/ │ │ │ └── Contents.json │ │ ├── plu_mdev.imageset/ │ │ │ └── Contents.json │ │ ├── plu_medv_n.imageset/ │ │ │ └── Contents.json │ │ ├── pluto.imageset/ │ │ │ └── Contents.json │ │ ├── plutoMessage.imageset/ │ │ │ └── Contents.json │ │ ├── plutoUpdate.imageset/ │ │ │ └── Contents.json │ │ ├── pluto_logo_n.imageset/ │ │ │ └── Contents.json │ │ ├── pluto_ring.imageset/ │ │ │ └── Contents.json │ │ ├── pop.imageset/ │ │ │ └── Contents.json │ │ ├── pop_bottom.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_bg.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_category_bottom.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_category_h.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_category_n.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_category_top.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_tag_bg.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_tag_bottom.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_tag_h.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_tag_n.imageset/ │ │ │ └── Contents.json │ │ ├── pop_sound_tag_top.imageset/ │ │ │ └── Contents.json │ │ ├── pop_top.imageset/ │ │ │ └── Contents.json │ │ ├── popicon.imageset/ │ │ │ └── Contents.json │ │ ├── popicon_history.imageset/ │ │ │ └── Contents.json │ │ ├── popicon_local.imageset/ │ │ │ └── Contents.json │ │ ├── popicon_play.imageset/ │ │ │ └── Contents.json │ │ ├── popupCoupon.imageset/ │ │ │ └── Contents.json │ │ ├── post_comment_line.imageset/ │ │ │ └── Contents.json │ │ ├── presentAlbumBack.imageset/ │ │ │ └── Contents.json │ │ ├── presentAlbum_change.imageset/ │ │ │ └── Contents.json │ │ ├── presentBuySuccess.imageset/ │ │ │ └── Contents.json │ │ ├── presentHolder.imageset/ │ │ │ └── Contents.json │ │ ├── presentMinus.imageset/ │ │ │ └── Contents.json │ │ ├── presentMinusOrange.imageset/ │ │ │ └── Contents.json │ │ ├── presentNotEnough.imageset/ │ │ │ └── Contents.json │ │ ├── presentPlus.imageset/ │ │ │ └── Contents.json │ │ ├── presentPlusOrange.imageset/ │ │ │ └── Contents.json │ │ ├── presentbg.imageset/ │ │ │ └── Contents.json │ │ ├── previous_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_Aplus.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_Q&A.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_Q&A_dis.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_fans.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_group.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_moneprofile_ic_refund.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_money.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_money_dis.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_nor.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_sel.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_share.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_star.imageset/ │ │ │ └── Contents.json │ │ ├── profile_ic_topfans.imageset/ │ │ │ └── Contents.json │ │ ├── promotelogo_dark.imageset/ │ │ │ └── Contents.json │ │ ├── promotelogo_light.imageset/ │ │ │ └── Contents.json │ │ ├── promptBanner.imageset/ │ │ │ └── Contents.json │ │ ├── promptNavi.imageset/ │ │ │ └── Contents.json │ │ ├── pull_to_refresh_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── purchased_bg_nocontent.imageset/ │ │ │ └── Contents.json │ │ ├── purchased_ic_more.imageset/ │ │ │ └── Contents.json │ │ ├── push_bg.imageset/ │ │ │ └── Contents.json │ │ ├── push_close.imageset/ │ │ │ └── Contents.json │ │ ├── push_open.imageset/ │ │ │ └── Contents.json │ │ ├── quiet.dataset/ │ │ │ ├── Contents.json │ │ │ └── quiet.caf │ │ ├── rank_tags_pickup.imageset/ │ │ │ └── Contents.json │ │ ├── reading_cat.imageset/ │ │ │ └── Contents.json │ │ ├── reading_tips2.imageset/ │ │ │ └── Contents.json │ │ ├── rebuy.imageset/ │ │ │ └── Contents.json │ │ ├── rec_bg_audio.imageset/ │ │ │ └── Contents.json │ │ ├── rec_bg_music.imageset/ │ │ │ └── Contents.json │ │ ├── rec_bg_music2.imageset/ │ │ │ └── Contents.json │ │ ├── rec_bg_screen3.imageset/ │ │ │ └── Contents.json │ │ ├── rec_bgimg_album.imageset/ │ │ │ └── Contents.json │ │ ├── rec_bgimg_voice.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_back.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_cancel.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_clip.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_clip_control.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_fontsize.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_mic_off.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_mic_on.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_music_off.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_music_on.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_music_pause.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_music_play.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_music_replace.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_preview.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_preview_control.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_preview_control2.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_preview_pause.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_preview_play.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_retry.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_save.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_vol.imageset/ │ │ │ └── Contents.json │ │ ├── rec_btn_volcontrol.imageset/ │ │ │ └── Contents.json │ │ ├── rec_dashed.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_mic_off.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_mic_on.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_music_add.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_music_off.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_music_on.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_tingyou_nor.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_tingyou_sel.imageset/ │ │ │ └── Contents.json │ │ ├── rec_ic_vol.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_logo.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_read1.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_read2.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_recoff.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_recon.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_tips.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_tips2.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_tips3.1.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_tips3.2.imageset/ │ │ │ └── Contents.json │ │ ├── rec_img_tips3.imageset/ │ │ │ └── Contents.json │ │ ├── rec_sd_bg.imageset/ │ │ │ └── Contents.json │ │ ├── rec_sd_img_highlight.imageset/ │ │ │ └── Contents.json │ │ ├── rec_sd_img_light.imageset/ │ │ │ └── Contents.json │ │ ├── recommedDefault.imageset/ │ │ │ └── Contents.json │ │ ├── recommendlogo.imageset/ │ │ │ └── Contents.json │ │ ├── record_album_add.imageset/ │ │ │ └── Contents.json │ │ ├── record_cancel_btn_h.imageset/ │ │ │ └── Contents.json │ │ ├── record_cancel_btn_n.imageset/ │ │ │ └── Contents.json │ │ ├── record_code_bg.imageset/ │ │ │ └── Contents.json │ │ ├── record_code_top_bg.imageset/ │ │ │ └── Contents.json │ │ ├── record_mark_check.imageset/ │ │ │ └── Contents.json │ │ ├── record_ok_btn_h.imageset/ │ │ │ └── Contents.json │ │ ├── record_ok_btn_n.imageset/ │ │ │ └── Contents.json │ │ ├── record_over.imageset/ │ │ │ └── Contents.json │ │ ├── record_photo_add.imageset/ │ │ │ └── Contents.json │ │ ├── record_share_tingfriend.imageset/ │ │ │ └── Contents.json │ │ ├── record_share_tingfriend_hl.imageset/ │ │ │ └── Contents.json │ │ ├── record_soundReport.imageset/ │ │ │ └── Contents.json │ │ ├── record_tingfriend.imageset/ │ │ │ └── Contents.json │ │ ├── record_tingfriend_h.imageset/ │ │ │ └── Contents.json │ │ ├── refund_ic.imageset/ │ │ │ └── Contents.json │ │ ├── refund_ic_adopt.imageset/ │ │ │ └── Contents.json │ │ ├── refund_ic_fail.imageset/ │ │ │ └── Contents.json │ │ ├── refund_ic_prompt.imageset/ │ │ │ └── Contents.json │ │ ├── refund_icon.imageset/ │ │ │ └── Contents.json │ │ ├── refund_icon_n.imageset/ │ │ │ └── Contents.json │ │ ├── refund_tips.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_code_textField.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_confirm_logo.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_nickname_icon.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_nickname_triangleLogo.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_password_hide_textfield.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_password_show_textfield.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_password_textField.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_qq_button.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_user_textField.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_wechat_button.imageset/ │ │ │ └── Contents.json │ │ ├── registerAndLogin_weibo_button.imageset/ │ │ │ └── Contents.json │ │ ├── regview_tab_r.imageset/ │ │ │ └── Contents.json │ │ ├── relatealbumtitle.imageset/ │ │ │ └── Contents.json │ │ ├── related_bg.imageset/ │ │ │ └── Contents.json │ │ ├── republish.imageset/ │ │ │ └── Contents.json │ │ ├── rewardButtonImage.imageset/ │ │ │ └── Contents.json │ │ ├── reward_bronze.imageset/ │ │ │ └── Contents.json │ │ ├── reward_gold.imageset/ │ │ │ └── Contents.json │ │ ├── reward_right.imageset/ │ │ │ └── Contents.json │ │ ├── reward_silver.imageset/ │ │ │ └── Contents.json │ │ ├── rf_pop_bg.imageset/ │ │ │ └── Contents.json │ │ ├── rock.imageset/ │ │ │ └── Contents.json │ │ ├── rockIcon.imageset/ │ │ │ └── Contents.json │ │ ├── rock_back.imageset/ │ │ │ └── Contents.json │ │ ├── rock_connect.imageset/ │ │ │ └── Contents.json │ │ ├── rock_small.imageset/ │ │ │ └── Contents.json │ │ ├── rock_wps.imageset/ │ │ │ └── Contents.json │ │ ├── ruler_pause.imageset/ │ │ │ └── Contents.json │ │ ├── ruler_play.imageset/ │ │ │ └── Contents.json │ │ ├── scanArea.imageset/ │ │ │ └── Contents.json │ │ ├── scanImage.imageset/ │ │ │ └── Contents.json │ │ ├── scan_scan.imageset/ │ │ │ └── Contents.json │ │ ├── screen_close.imageset/ │ │ │ └── Contents.json │ │ ├── screen_feedback.imageset/ │ │ │ └── Contents.json │ │ ├── screen_share.imageset/ │ │ │ └── Contents.json │ │ ├── sct_backhome.imageset/ │ │ │ └── Contents.json │ │ ├── sct_settingbtn.imageset/ │ │ │ └── Contents.json │ │ ├── sct_step1.imageset/ │ │ │ └── Contents.json │ │ ├── sct_step2.imageset/ │ │ │ └── Contents.json │ │ ├── sct_step3.imageset/ │ │ │ └── Contents.json │ │ ├── sctshare_default.imageset/ │ │ │ └── Contents.json │ │ ├── search_album_download.imageset/ │ │ │ └── Contents.json │ │ ├── search_album_downloadw.imageset/ │ │ │ └── Contents.json │ │ ├── search_album_icon.imageset/ │ │ │ └── Contents.json │ │ ├── search_back.imageset/ │ │ │ └── Contents.json │ │ ├── search_back_h.imageset/ │ │ │ └── Contents.json │ │ ├── search_bar_bg.imageset/ │ │ │ └── Contents.json │ │ ├── search_btn_hl.imageset/ │ │ │ └── Contents.json │ │ ├── search_btn_nl.imageset/ │ │ │ └── Contents.json │ │ ├── search_btn_norm.imageset/ │ │ │ └── Contents.json │ │ ├── search_category_icon.imageset/ │ │ │ └── Contents.json │ │ ├── search_correct_btn.imageset/ │ │ │ └── Contents.json │ │ ├── search_detail_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── search_filter.imageset/ │ │ │ └── Contents.json │ │ ├── search_filter_bg.imageset/ │ │ │ └── Contents.json │ │ ├── search_filter_bg_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── search_filter_btn_sel.imageset/ │ │ │ └── Contents.json │ │ ├── search_filter_h.imageset/ │ │ │ └── Contents.json │ │ ├── search_header_bg.imageset/ │ │ │ └── Contents.json │ │ ├── search_history_close.imageset/ │ │ │ └── Contents.json │ │ ├── search_intent_category_icon.imageset/ │ │ │ └── Contents.json │ │ ├── search_new_mark.imageset/ │ │ │ └── Contents.json │ │ ├── search_normal_icon.imageset/ │ │ │ └── Contents.json │ │ ├── search_play.imageset/ │ │ │ └── Contents.json │ │ ├── search_play_count.imageset/ │ │ │ └── Contents.json │ │ ├── search_tag_hot.imageset/ │ │ │ └── Contents.json │ │ ├── search_tag_recommend.imageset/ │ │ │ └── Contents.json │ │ ├── search_tags.imageset/ │ │ │ └── Contents.json │ │ ├── search_tags_red.imageset/ │ │ │ └── Contents.json │ │ ├── search_textfield_bg.imageset/ │ │ │ └── Contents.json │ │ ├── secret.imageset/ │ │ │ └── Contents.json │ │ ├── seg_category_finish.imageset/ │ │ │ └── Contents.json │ │ ├── seg_down.imageset/ │ │ │ └── Contents.json │ │ ├── seg_downloaded.imageset/ │ │ │ └── Contents.json │ │ ├── seg_filter_down.imageset/ │ │ │ └── Contents.json │ │ ├── seg_filter_pulldown_n.imageset/ │ │ │ └── Contents.json │ │ ├── seg_filter_pullup_n.imageset/ │ │ │ └── Contents.json │ │ ├── seg_filter_up.imageset/ │ │ │ └── Contents.json │ │ ├── seg_mask.imageset/ │ │ │ └── Contents.json │ │ ├── seg_pulldown_n.imageset/ │ │ │ └── Contents.json │ │ ├── seg_pullup_n.imageset/ │ │ │ └── Contents.json │ │ ├── seg_selected.imageset/ │ │ │ └── Contents.json │ │ ├── seg_selectedAll.imageset/ │ │ │ └── Contents.json │ │ ├── seg_unselected.imageset/ │ │ │ └── Contents.json │ │ ├── seg_unselectedAll.imageset/ │ │ │ └── Contents.json │ │ ├── seg_up.imageset/ │ │ │ └── Contents.json │ │ ├── segline.imageset/ │ │ │ └── Contents.json │ │ ├── segmentShadowLine.imageset/ │ │ │ └── Contents.json │ │ ├── sendletter_fail_bg.imageset/ │ │ │ └── Contents.json │ │ ├── setting_bt_icon.imageset/ │ │ │ └── Contents.json │ │ ├── setting_bt_tone.imageset/ │ │ │ └── Contents.json │ │ ├── setting_carmode.imageset/ │ │ │ └── Contents.json │ │ ├── setting_email.imageset/ │ │ │ └── Contents.json │ │ ├── setting_help.imageset/ │ │ │ └── Contents.json │ │ ├── setting_line.imageset/ │ │ │ └── Contents.json │ │ ├── setting_password.imageset/ │ │ │ └── Contents.json │ │ ├── setting_password_bg.imageset/ │ │ │ └── Contents.json │ │ ├── setting_phone.imageset/ │ │ │ └── Contents.json │ │ ├── setting_qq.imageset/ │ │ │ └── Contents.json │ │ ├── setting_rate.imageset/ │ │ │ └── Contents.json │ │ ├── setting_renren.imageset/ │ │ │ └── Contents.json │ │ ├── setting_sina.imageset/ │ │ │ └── Contents.json │ │ ├── setting_sm_icon.imageset/ │ │ │ └── Contents.json │ │ ├── setting_wechat.imageset/ │ │ │ └── Contents.json │ │ ├── shadow_albumView_header.imageset/ │ │ │ └── Contents.json │ │ ├── shake.dataset/ │ │ │ └── Contents.json │ │ ├── shakeLottoryNPViewIcon.imageset/ │ │ │ └── Contents.json │ │ ├── shakeLottoryTipBg.imageset/ │ │ │ └── Contents.json │ │ ├── share_QRcode.imageset/ │ │ │ └── Contents.json │ │ ├── share_btn_user.imageset/ │ │ │ └── Contents.json │ │ ├── share_btn_user_h.imageset/ │ │ │ └── Contents.json │ │ ├── share_btn_user_n.imageset/ │ │ │ └── Contents.json │ │ ├── share_code_bg.imageset/ │ │ │ └── Contents.json │ │ ├── share_code_default.imageset/ │ │ │ └── Contents.json │ │ ├── share_code_icon_bg.imageset/ │ │ │ └── Contents.json │ │ ├── share_code_mask_black.imageset/ │ │ │ └── Contents.json │ │ ├── share_copy.imageset/ │ │ │ └── Contents.json │ │ ├── share_default.imageset/ │ │ │ └── Contents.json │ │ ├── share_group.imageset/ │ │ │ └── Contents.json │ │ ├── share_html.imageset/ │ │ │ └── Contents.json │ │ ├── share_loginIV.imageset/ │ │ │ └── Contents.json │ │ ├── share_qq.imageset/ │ │ │ └── Contents.json │ │ ├── share_qq_zone.imageset/ │ │ │ └── Contents.json │ │ ├── share_renren.imageset/ │ │ │ └── Contents.json │ │ ├── share_save.imageset/ │ │ │ └── Contents.json │ │ ├── share_sina_n.imageset/ │ │ │ └── Contents.json │ │ ├── share_sms_n.imageset/ │ │ │ └── Contents.json │ │ ├── share_tencent_n.imageset/ │ │ │ └── Contents.json │ │ ├── share_tingfriend.imageset/ │ │ │ └── Contents.json │ │ ├── share_toast_bg.imageset/ │ │ │ └── Contents.json │ │ ├── share_triangle.imageset/ │ │ │ └── Contents.json │ │ ├── share_weixin.imageset/ │ │ │ └── Contents.json │ │ ├── share_weixin_friends.imageset/ │ │ │ └── Contents.json │ │ ├── sharepop_cancel_bg.imageset/ │ │ │ └── Contents.json │ │ ├── shareting.imageset/ │ │ │ └── Contents.json │ │ ├── shouhuan.imageset/ │ │ │ └── Contents.json │ │ ├── showPwd.imageset/ │ │ │ └── Contents.json │ │ ├── shuke.imageset/ │ │ │ └── Contents.json │ │ ├── signview_bg.imageset/ │ │ │ └── Contents.json │ │ ├── single_unfollow.imageset/ │ │ │ └── Contents.json │ │ ├── slidePath.imageset/ │ │ │ └── Contents.json │ │ ├── slide_hand.imageset/ │ │ │ └── Contents.json │ │ ├── small_head_male_default.imageset/ │ │ │ └── Contents.json │ │ ├── sortTips.imageset/ │ │ │ └── Contents.json │ │ ├── sort_list.imageset/ │ │ │ └── Contents.json │ │ ├── soundImage_bg.imageset/ │ │ │ └── Contents.json │ │ ├── soundLB_info.imageset/ │ │ │ └── Contents.json │ │ ├── soundLB_video.imageset/ │ │ │ └── Contents.json │ │ ├── sound_act.imageset/ │ │ │ └── Contents.json │ │ ├── sound_act_s.imageset/ │ │ │ └── Contents.json │ │ ├── sound_album.imageset/ │ │ │ └── Contents.json │ │ ├── sound_albumcover.imageset/ │ │ │ └── Contents.json │ │ ├── sound_albumcover_large.imageset/ │ │ │ └── Contents.json │ │ ├── sound_comments.imageset/ │ │ │ └── Contents.json │ │ ├── sound_cover_bg.imageset/ │ │ │ └── Contents.json │ │ ├── sound_default.imageset/ │ │ │ └── Contents.json │ │ ├── sound_downloading.imageset/ │ │ │ └── Contents.json │ │ ├── sound_duration.imageset/ │ │ │ └── Contents.json │ │ ├── sound_failed.imageset/ │ │ │ └── Contents.json │ │ ├── sound_feed_more.imageset/ │ │ │ └── Contents.json │ │ ├── sound_feed_remove.imageset/ │ │ │ └── Contents.json │ │ ├── sound_feed_sticky.imageset/ │ │ │ └── Contents.json │ │ ├── sound_feed_sticky_cancel.imageset/ │ │ │ └── Contents.json │ │ ├── sound_feed_top.imageset/ │ │ │ └── Contents.json │ │ ├── sound_likes.imageset/ │ │ │ └── Contents.json │ │ ├── sound_likes_n.imageset/ │ │ │ └── Contents.json │ │ ├── sound_lock.imageset/ │ │ │ └── Contents.json │ │ ├── sound_paused.imageset/ │ │ │ └── Contents.json │ │ ├── sound_play.imageset/ │ │ │ └── Contents.json │ │ ├── sound_playbtn.imageset/ │ │ │ └── Contents.json │ │ ├── sound_playingbtn.imageset/ │ │ │ └── Contents.json │ │ ├── sound_playtimes.imageset/ │ │ │ └── Contents.json │ │ ├── sound_processing.imageset/ │ │ │ └── Contents.json │ │ ├── sound_progress_h.imageset/ │ │ │ └── Contents.json │ │ ├── sound_progress_n.imageset/ │ │ │ └── Contents.json │ │ ├── sound_repost.imageset/ │ │ │ └── Contents.json │ │ ├── sound_sizes.imageset/ │ │ │ └── Contents.json │ │ ├── sound_state_bg.imageset/ │ │ │ └── Contents.json │ │ ├── sound_tags.imageset/ │ │ │ └── Contents.json │ │ ├── sound_tags_s.imageset/ │ │ │ └── Contents.json │ │ ├── sound_uploading.imageset/ │ │ │ └── Contents.json │ │ ├── sound_waiting.imageset/ │ │ │ └── Contents.json │ │ ├── splitLine.imageset/ │ │ │ └── Contents.json │ │ ├── splitLine_expired.imageset/ │ │ │ └── Contents.json │ │ ├── sponsorBtn.imageset/ │ │ │ └── Contents.json │ │ ├── sponsorBtn4Cell.imageset/ │ │ │ └── Contents.json │ │ ├── sponsorLogo.imageset/ │ │ │ └── Contents.json │ │ ├── sponsorMore4Cell.imageset/ │ │ │ └── Contents.json │ │ ├── sponsorTop1.imageset/ │ │ │ └── Contents.json │ │ ├── sponsorTop2.imageset/ │ │ │ └── Contents.json │ │ ├── sponsorTop3.imageset/ │ │ │ └── Contents.json │ │ ├── sr_cell_bg_h.imageset/ │ │ │ └── Contents.json │ │ ├── star-on.imageset/ │ │ │ └── Contents.json │ │ ├── start_all_h.imageset/ │ │ │ └── Contents.json │ │ ├── start_all_n.imageset/ │ │ │ └── Contents.json │ │ ├── style.dataset/ │ │ │ ├── Contents.json │ │ │ └── style.css │ │ ├── sublevel_tab_back_h.imageset/ │ │ │ └── Contents.json │ │ ├── sublevel_tab_back_n.imageset/ │ │ │ └── Contents.json │ │ ├── sublevel_tab_home_h.imageset/ │ │ │ └── Contents.json │ │ ├── sublevel_tab_home_n.imageset/ │ │ │ └── Contents.json │ │ ├── suicheting.imageset/ │ │ │ └── Contents.json │ │ ├── tabPlaceImage.imageset/ │ │ │ └── Contents.json │ │ ├── tab_search.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_bg.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_bg_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_Rss_normal.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_Rss_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_find_normal.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_find_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_hear_normal.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_hear_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_home_normal.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_home_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_mine_normal.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_icon_mine_pressed.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_np_loop.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_np_normal.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_np_play.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_np_playnon.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_np_playshadow.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_np_shadow.imageset/ │ │ │ └── Contents.json │ │ ├── tableViewMask.imageset/ │ │ │ └── Contents.json │ │ ├── tag_all.imageset/ │ │ │ └── Contents.json │ │ ├── tag_default.imageset/ │ │ │ └── Contents.json │ │ ├── tagbar_bg.imageset/ │ │ │ └── Contents.json │ │ ├── tagbar_sel.imageset/ │ │ │ └── Contents.json │ │ ├── tagsViewLine.imageset/ │ │ │ └── Contents.json │ │ ├── testEnvironment.imageset/ │ │ │ └── Contents.json │ │ ├── third_app_default.imageset/ │ │ │ └── Contents.json │ │ ├── ting.dataset/ │ │ │ ├── Contents.json │ │ │ └── ting │ │ ├── ting_icon.imageset/ │ │ │ └── Contents.json │ │ ├── ting_sound_uploading.imageset/ │ │ │ └── Contents.json │ │ ├── tingshubao.imageset/ │ │ │ └── Contents.json │ │ ├── tingshubao_logo.imageset/ │ │ │ └── Contents.json │ │ ├── titleBorder.imageset/ │ │ │ └── Contents.json │ │ ├── titleBorder_gray.imageset/ │ │ │ └── Contents.json │ │ ├── title_ic_findfriend_n.imageset/ │ │ │ └── Contents.json │ │ ├── title_ic_kf.imageset/ │ │ │ └── Contents.json │ │ ├── title_ic_more_n.imageset/ │ │ │ └── Contents.json │ │ ├── title_ic_release_n.imageset/ │ │ │ └── Contents.json │ │ ├── toast_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── toast_bg.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_clock_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_clock_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_danmu_close.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_danmu_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_danmu_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_history_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_history_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_history_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_history_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_like_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_like_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_loading_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_loading_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_loading_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_loading_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_more_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_more_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_next_d.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_next_d_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_next_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_next_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_next_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_next_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_pause_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_pause_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_pause_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_pause_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_play_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_play_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_play_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_play_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_playinglist_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_playinglist_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_playinglist_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_playinglist_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_prev_d.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_prev_d_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_prev_h.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_prev_h_p.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_prev_n.imageset/ │ │ │ └── Contents.json │ │ ├── toolbar_prev_n_p.imageset/ │ │ │ └── Contents.json │ │ ├── top_addlike_n.imageset/ │ │ │ └── Contents.json │ │ ├── top_download_n.imageset/ │ │ │ └── Contents.json │ │ ├── top_filter.imageset/ │ │ │ └── Contents.json │ │ ├── top_history_n.imageset/ │ │ │ └── Contents.json │ │ ├── top_logo.imageset/ │ │ │ └── Contents.json │ │ ├── top_message_n.imageset/ │ │ │ └── Contents.json │ │ ├── top_search.imageset/ │ │ │ └── Contents.json │ │ ├── top_search_bg.imageset/ │ │ │ └── Contents.json │ │ ├── trackBuy_alert_icon.imageset/ │ │ │ └── Contents.json │ │ ├── track_album_cover_bg.imageset/ │ │ │ └── Contents.json │ │ ├── track_album_cover_bg_mask.imageset/ │ │ │ └── Contents.json │ │ ├── track_alert_image.imageset/ │ │ │ └── Contents.json │ │ ├── traffic_bg.imageset/ │ │ │ └── Contents.json │ │ ├── traffic_close.imageset/ │ │ │ └── Contents.json │ │ ├── trylisten_disable.imageset/ │ │ │ └── Contents.json │ │ ├── trylisten_stop.imageset/ │ │ │ └── Contents.json │ │ ├── tsb_connect_mode.imageset/ │ │ │ └── Contents.json │ │ ├── tyq_ic_addlist.imageset/ │ │ │ └── Contents.json │ │ ├── tyq_ic_addlist_s.imageset/ │ │ │ └── Contents.json │ │ ├── tyq_ic_more.imageset/ │ │ │ └── Contents.json │ │ ├── tyq_ic_release.imageset/ │ │ │ └── Contents.json │ │ ├── tyq_ic_weibo.imageset/ │ │ │ └── Contents.json │ │ ├── tyq_ic_weibo_s.imageset/ │ │ │ └── Contents.json │ │ ├── tyq_notice_ic_check.imageset/ │ │ │ └── Contents.json │ │ ├── uithread.imageset/ │ │ │ └── Contents.json │ │ ├── unUsingFreeThough.imageset/ │ │ │ └── Contents.json │ │ ├── underscore.min.dataset/ │ │ │ └── Contents.json │ │ ├── unfolded_arrow.imageset/ │ │ │ └── Contents.json │ │ ├── unpublished.imageset/ │ │ │ └── Contents.json │ │ ├── unpublishedMore.imageset/ │ │ │ └── Contents.json │ │ ├── up_n.imageset/ │ │ │ └── Contents.json │ │ ├── upgrade_icon.imageset/ │ │ │ └── Contents.json │ │ ├── userBackHS.imageset/ │ │ │ └── Contents.json │ │ ├── userBackHi.imageset/ │ │ │ └── Contents.json │ │ ├── userBackNS.imageset/ │ │ │ └── Contents.json │ │ ├── userBackNor.imageset/ │ │ │ └── Contents.json │ │ ├── userBottomFol.imageset/ │ │ │ └── Contents.json │ │ ├── userBottomMes.imageset/ │ │ │ └── Contents.json │ │ ├── userBottomUnFol.imageset/ │ │ │ └── Contents.json │ │ ├── userEditCell.imageset/ │ │ │ └── Contents.json │ │ ├── userEditH.imageset/ │ │ │ └── Contents.json │ │ ├── userEditNS.imageset/ │ │ │ └── Contents.json │ │ ├── userEditNor.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV0.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV1.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV10.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV2.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV3.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV4.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV5.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV6.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV7.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV8.imageset/ │ │ │ └── Contents.json │ │ ├── userGradeV9.imageset/ │ │ │ └── Contents.json │ │ ├── userMoreHS.imageset/ │ │ │ └── Contents.json │ │ ├── userMoreHi.imageset/ │ │ │ └── Contents.json │ │ ├── userMoreNS.imageset/ │ │ │ └── Contents.json │ │ ├── userMoreNor.imageset/ │ │ │ └── Contents.json │ │ ├── userRank.imageset/ │ │ │ └── Contents.json │ │ ├── userShareHS.imageset/ │ │ │ └── Contents.json │ │ ├── userShareHi.imageset/ │ │ │ └── Contents.json │ │ ├── userShareNS.imageset/ │ │ │ └── Contents.json │ │ ├── userShareNor.imageset/ │ │ │ └── Contents.json │ │ ├── userSponsor.imageset/ │ │ │ └── Contents.json │ │ ├── user_Aquarius.imageset/ │ │ │ └── Contents.json │ │ ├── user_Aries.imageset/ │ │ │ └── Contents.json │ │ ├── user_Cancer.imageset/ │ │ │ └── Contents.json │ │ ├── user_Capricornus.imageset/ │ │ │ └── Contents.json │ │ ├── user_Gemini.imageset/ │ │ │ └── Contents.json │ │ ├── user_Leo.imageset/ │ │ │ └── Contents.json │ │ ├── user_Libra.imageset/ │ │ │ └── Contents.json │ │ ├── user_Pisces.imageset/ │ │ │ └── Contents.json │ │ ├── user_QA.imageset/ │ │ │ └── Contents.json │ │ ├── user_Sagittarius.imageset/ │ │ │ └── Contents.json │ │ ├── user_Scorpio.imageset/ │ │ │ └── Contents.json │ │ ├── user_Taurus.imageset/ │ │ │ └── Contents.json │ │ ├── user_VIP.imageset/ │ │ │ └── Contents.json │ │ ├── user_Virgo.imageset/ │ │ │ └── Contents.json │ │ ├── user_edit_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_edit_btn_h.imageset/ │ │ │ └── Contents.json │ │ ├── user_edit_btn_n.imageset/ │ │ │ └── Contents.json │ │ ├── user_fans.imageset/ │ │ │ └── Contents.json │ │ ├── user_female.imageset/ │ │ │ └── Contents.json │ │ ├── user_focus_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_focused_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_head.imageset/ │ │ │ └── Contents.json │ │ ├── user_head_VIP_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_head_comment.imageset/ │ │ │ └── Contents.json │ │ ├── user_head_hotlineVIP_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_head_hotline_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_hind_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_location.imageset/ │ │ │ └── Contents.json │ │ ├── user_mail_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_male.imageset/ │ │ │ └── Contents.json │ │ ├── user_moreicon.imageset/ │ │ │ └── Contents.json │ │ ├── user_publish_btn.imageset/ │ │ │ └── Contents.json │ │ ├── user_ranking 6.imageset/ │ │ │ └── Contents.json │ │ ├── user_sponsor.imageset/ │ │ │ └── Contents.json │ │ ├── usingFreeThough.imageset/ │ │ │ └── Contents.json │ │ ├── view_bg.imageset/ │ │ │ └── Contents.json │ │ ├── vip_get_btn.imageset/ │ │ │ └── Contents.json │ │ ├── vip_guide_bg.imageset/ │ │ │ └── Contents.json │ │ ├── vip_guide_close.imageset/ │ │ │ └── Contents.json │ │ ├── vip_icon.imageset/ │ │ │ └── Contents.json │ │ ├── vip_ident_large.imageset/ │ │ │ └── Contents.json │ │ ├── voicesearch__ic_mic.imageset/ │ │ │ └── Contents.json │ │ ├── voicesearch_bg_oval.imageset/ │ │ │ └── Contents.json │ │ ├── voicesearch_btn_confirm_nor.imageset/ │ │ │ └── Contents.json │ │ ├── voicesearch_btn_confirm_pre.imageset/ │ │ │ └── Contents.json │ │ ├── voicesearch_btn_rec_nor.imageset/ │ │ │ └── Contents.json │ │ ├── voicesearch_btn_rec_pre.imageset/ │ │ │ └── Contents.json │ │ ├── voicesearch_img_loading.imageset/ │ │ │ └── Contents.json │ │ ├── voucher_close.imageset/ │ │ │ └── Contents.json │ │ ├── voucher_horizontal_line.imageset/ │ │ │ └── Contents.json │ │ ├── voucher_line.imageset/ │ │ │ └── Contents.json │ │ ├── voucher_red.imageset/ │ │ │ └── Contents.json │ │ ├── voucher_red_u.imageset/ │ │ │ └── Contents.json │ │ ├── voucher_select.imageset/ │ │ │ └── Contents.json │ │ ├── voucher_unselect.imageset/ │ │ │ └── Contents.json │ │ ├── w_fuyun.imageset/ │ │ │ └── Contents.json │ │ ├── w_luoye.imageset/ │ │ │ └── Contents.json │ │ ├── w_shachenbao.imageset/ │ │ │ └── Contents.json │ │ ├── w_taiyang.imageset/ │ │ │ └── Contents.json │ │ ├── w_weifeng.imageset/ │ │ │ └── Contents.json │ │ ├── w_xianhua.imageset/ │ │ │ └── Contents.json │ │ ├── w_xiayu.imageset/ │ │ │ └── Contents.json │ │ ├── w_xue.imageset/ │ │ │ └── Contents.json │ │ ├── w_xueren.imageset/ │ │ │ └── Contents.json │ │ ├── w_yueliang.imageset/ │ │ │ └── Contents.json │ │ ├── wd_more_ic_clock.imageset/ │ │ │ └── Contents.json │ │ ├── wd_more_ic_stopwatch.imageset/ │ │ │ └── Contents.json │ │ ├── web_btn_back.imageset/ │ │ │ └── Contents.json │ │ ├── web_btn_close.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_bind.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_binded.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_connect_pic.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_dis_connect_pic.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_nodevice.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_nosuo.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_psw.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_psw_bg.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_psw_ok.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_retry_btn.imageset/ │ │ │ └── Contents.json │ │ ├── wifi_suo.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_channel1.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_channel2.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_channel3.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_channel4.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_checkmark.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_down.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_edit.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_nextstep.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_play.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_play_h.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_step1.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_step2.imageset/ │ │ │ └── Contents.json │ │ ├── wifibox_top.imageset/ │ │ │ └── Contents.json │ │ ├── wifilogo.imageset/ │ │ │ └── Contents.json │ │ ├── xim_eye_off.imageset/ │ │ │ └── Contents.json │ │ ├── xim_eye_on.imageset/ │ │ │ └── Contents.json │ │ ├── ximalaya_carlogo.imageset/ │ │ │ └── Contents.json │ │ ├── xmToolBox.imageset/ │ │ │ └── Contents.json │ │ ├── xm_download.imageset/ │ │ │ └── Contents.json │ │ ├── xm_history.imageset/ │ │ │ └── Contents.json │ │ ├── xm_popular.imageset/ │ │ │ └── Contents.json │ │ ├── xm_recommendation.imageset/ │ │ │ └── Contents.json │ │ ├── xm_subscription.imageset/ │ │ │ └── Contents.json │ │ ├── xmtoolboxclose.imageset/ │ │ │ └── Contents.json │ │ ├── xmtoolboxtitle.imageset/ │ │ │ └── Contents.json │ │ ├── xplay_cancel.imageset/ │ │ │ └── Contents.json │ │ ├── yijiantingGuide.imageset/ │ │ │ └── Contents.json │ │ ├── yijiantingProcessBg.imageset/ │ │ │ └── Contents.json │ │ ├── yijiantingplayProcessPc.imageset/ │ │ │ └── Contents.json │ │ ├── yjtlisten_w.imageset/ │ │ │ └── Contents.json │ │ ├── yjtloading.imageset/ │ │ │ └── Contents.json │ │ ├── yjtmask.imageset/ │ │ │ └── Contents.json │ │ ├── yjtnext_h.imageset/ │ │ │ └── Contents.json │ │ ├── yjtnext_n.imageset/ │ │ │ └── Contents.json │ │ ├── yjtpause_h.imageset/ │ │ │ └── Contents.json │ │ ├── yjtpause_n.imageset/ │ │ │ └── Contents.json │ │ ├── yjtplay_h.imageset/ │ │ │ └── Contents.json │ │ ├── yjtplay_n.imageset/ │ │ │ └── Contents.json │ │ ├── yjtprocessTag.imageset/ │ │ │ └── Contents.json │ │ ├── yjtslogan.imageset/ │ │ │ └── Contents.json │ │ ├── zone_deletepic.imageset/ │ │ │ └── Contents.json │ │ ├── 删除.imageset/ │ │ │ └── Contents.json │ │ ├── 声音iconhome_ic_banner_sound.imageset/ │ │ │ └── Contents.json │ │ ├── 播放home_ic_banner_paly_big.imageset/ │ │ │ └── Contents.json │ │ ├── 播放标home_ic_banner_paly_small.imageset/ │ │ │ └── Contents.json │ │ └── 流程.imageset/ │ │ └── Contents.json │ ├── Classes/ │ │ ├── Common/ │ │ │ ├── Common.swift │ │ │ ├── CustomUI/ │ │ │ │ ├── HCBaseNavigationController/ │ │ │ │ │ └── HCBaseNavigationController.swift │ │ │ │ ├── HCBaseViewController/ │ │ │ │ │ └── HCBaseViewController.swift │ │ │ │ ├── HCRefreshGifHeader/ │ │ │ │ │ └── HCRefreshGifHeader.swift │ │ │ │ └── HCScrollBarView/ │ │ │ │ ├── HCScrollBarCell.swift │ │ │ │ ├── HCScrollBarCell.xib │ │ │ │ └── HCScrollBarView.swift │ │ │ ├── Extension/ │ │ │ │ ├── RxAlamofire+ObjectMapper.swift │ │ │ │ ├── String+GetSize.swift │ │ │ │ ├── UIColor+HexColor.swift │ │ │ │ ├── UIImage+Color.swift │ │ │ │ ├── UINavigationBar+ChangeColor.swift │ │ │ │ ├── UIView+Corner.swift │ │ │ │ └── UIView+Frame.swift │ │ │ ├── JsonTemplate.txt │ │ │ ├── Manager/ │ │ │ │ └── HCURLNavigatorManager.swift │ │ │ ├── Protocol/ │ │ │ │ ├── HCCellStyleable/ │ │ │ │ │ └── HCCellStyleable.swift │ │ │ │ ├── MJRefresh/ │ │ │ │ │ └── HCRefreshable.swift │ │ │ │ ├── UINavigationBar/ │ │ │ │ │ ├── HCNavBackable.swift │ │ │ │ │ ├── HCNavDownloadable.swift │ │ │ │ │ ├── HCNavHistoryable.swift │ │ │ │ │ ├── HCNavMessageable.swift │ │ │ │ │ ├── HCNavSearchable.swift │ │ │ │ │ ├── HCNavSettingable.swift │ │ │ │ │ ├── HCNavTitleable.swift │ │ │ │ │ └── HCNavUniversalable.swift │ │ │ │ ├── UIView/ │ │ │ │ │ ├── HCMineAnchorsable/ │ │ │ │ │ │ └── HCMineAnchorsable.swift │ │ │ │ │ ├── HCNibloadable/ │ │ │ │ │ │ └── HCNibloadable.swift │ │ │ │ │ └── HCSearchBarable/ │ │ │ │ │ ├── HCHomeSearchBarable.swift │ │ │ │ │ └── HCSearchControllerable.swift │ │ │ │ └── ViewModel/ │ │ │ │ └── HCViewModelType.swift │ │ │ ├── RequestUrl.swift │ │ │ ├── Tools/ │ │ │ │ ├── HCInputValidator.swift │ │ │ │ └── HCTimeTools.swift │ │ │ └── play_synopsis.html │ │ ├── Main/ │ │ │ ├── Find/ │ │ │ │ └── HCFindViewController.swift │ │ │ ├── HCMainViewController.swift │ │ │ ├── Hear/ │ │ │ │ └── HCHearViewController.swift │ │ │ ├── Home/ │ │ │ │ ├── Controller/ │ │ │ │ │ ├── Boutique(精品)/ │ │ │ │ │ │ ├── FlowLayout/ │ │ │ │ │ │ │ ├── HCBoutiqueIndexFlowLayout.swift │ │ │ │ │ │ │ └── HCBoutiqueSingleIndexFlowLayout.swift │ │ │ │ │ │ ├── HCBoutiqueViewController.swift │ │ │ │ │ │ ├── Model/ │ │ │ │ │ │ │ ├── HCBoutiqueIndexModel.swift │ │ │ │ │ │ │ └── HCBoutiqueModel.swift │ │ │ │ │ │ ├── View/ │ │ │ │ │ │ │ ├── Cell/ │ │ │ │ │ │ │ │ ├── HCBoutiqueIndexCell.swift │ │ │ │ │ │ │ │ └── HCBoutiqueIndexCell.xib │ │ │ │ │ │ │ ├── HCBoutiqueFooterView.swift │ │ │ │ │ │ │ ├── HCBoutiqueFooterView.xib │ │ │ │ │ │ │ ├── HCBoutiqueIndexHeaderView.swift │ │ │ │ │ │ │ ├── HCBoutiqueIndexHeaderView.xib │ │ │ │ │ │ │ ├── HCBoutiqueSingleHeaderView.swift │ │ │ │ │ │ │ └── HCBoutiqueSingleHeaderView.xib │ │ │ │ │ │ └── ViewModel/ │ │ │ │ │ │ └── HCBoutiqueViewModel.swift │ │ │ │ │ ├── Recommend(推荐)/ │ │ │ │ │ │ ├── FlowLayout/ │ │ │ │ │ │ │ ├── HCRecommendFlowLayout.swift │ │ │ │ │ │ │ └── HCSquareFlowLayout.swift │ │ │ │ │ │ ├── HCRecommendViewController.swift │ │ │ │ │ │ ├── Model/ │ │ │ │ │ │ │ ├── HCActivityModel.swift │ │ │ │ │ │ │ ├── HCCategoryModel.swift │ │ │ │ │ │ │ ├── HCFocusModel.swift │ │ │ │ │ │ │ ├── HCKeywardsModel.swift │ │ │ │ │ │ │ ├── HCRecommendCellTypeModel.swift │ │ │ │ │ │ │ ├── HCRecommendItemModel.swift │ │ │ │ │ │ │ ├── HCRecommendModel.swift │ │ │ │ │ │ │ └── HCSquareModel.swift │ │ │ │ │ │ ├── View/ │ │ │ │ │ │ │ ├── Cell/ │ │ │ │ │ │ │ │ ├── HCRecommendCell.swift │ │ │ │ │ │ │ │ ├── HCRecommendCell.xib │ │ │ │ │ │ │ │ ├── HCRecommendSingleCell.swift │ │ │ │ │ │ │ │ ├── HCRecommendSingleCell.xib │ │ │ │ │ │ │ │ ├── HCSquareCell.swift │ │ │ │ │ │ │ │ └── HCSquareCell.xib │ │ │ │ │ │ │ ├── HCRecommendFooterView.swift │ │ │ │ │ │ │ ├── HCRecommendFooterView.xib │ │ │ │ │ │ │ ├── HCRecommendHeaderView.swift │ │ │ │ │ │ │ ├── HCRecommendHeaderView.xib │ │ │ │ │ │ │ ├── HCRecommendTopHeaderView.swift │ │ │ │ │ │ │ └── HCRecommendTopHeaderView.xib │ │ │ │ │ │ └── ViewModel/ │ │ │ │ │ │ └── HCRecommendViewModel.swift │ │ │ │ │ └── Search(搜索)/ │ │ │ │ │ ├── HCSearchController.swift │ │ │ │ │ ├── HCSearchResultController.swift │ │ │ │ │ └── View/ │ │ │ │ │ └── HCSearchNavigationBar.swift │ │ │ │ ├── HCHomeViewController.swift │ │ │ │ └── View/ │ │ │ │ └── HCHomeNavigationBar.swift │ │ │ ├── Mine/ │ │ │ │ ├── Controller/ │ │ │ │ │ ├── Login/ │ │ │ │ │ │ ├── Controller/ │ │ │ │ │ │ │ ├── HCAccountLoginViewController.swift │ │ │ │ │ │ │ └── HCThridLoginViewController.swift │ │ │ │ │ │ ├── HCLoginViewController.swift │ │ │ │ │ │ ├── HCThridLoginViewController.swift │ │ │ │ │ │ ├── Model/ │ │ │ │ │ │ │ └── HCAccountLoginResult.swift │ │ │ │ │ │ ├── Service/ │ │ │ │ │ │ │ └── HCAccountLoginService.swift │ │ │ │ │ │ ├── View/ │ │ │ │ │ │ │ ├── HCAccountLoginable.swift │ │ │ │ │ │ │ ├── HCOtherLoginModeView.swift │ │ │ │ │ │ │ └── HCOtherLoginModeView.xib │ │ │ │ │ │ └── ViewModel/ │ │ │ │ │ │ └── HCAccountLoginViewModel.swift │ │ │ │ │ └── Setting/ │ │ │ │ │ ├── HCSettingViewController.swift │ │ │ │ │ ├── Model/ │ │ │ │ │ │ └── HCSettingCellModel.swift │ │ │ │ │ ├── View/ │ │ │ │ │ │ └── HCSettingCell.swift │ │ │ │ │ └── ViewModel/ │ │ │ │ │ └── HCSettingViewModel.swift │ │ │ │ ├── HCMineFactory.swift │ │ │ │ ├── HCMineViewController.swift │ │ │ │ └── View/ │ │ │ │ ├── HCMineHeaderView.swift │ │ │ │ ├── HCMineHeaderView.xib │ │ │ │ └── HCMineNavigationBar.swift │ │ │ └── Play/ │ │ │ ├── HCPlayViewController.swift │ │ │ ├── Model/ │ │ │ │ ├── HCAlbumInfoModel.swift │ │ │ │ ├── HCAssociationAlbumsInfoModel.swift │ │ │ │ ├── HCNoCacheInfoModel.swift │ │ │ │ ├── HCPlayCellModel.swift │ │ │ │ ├── HCPlayModel.swift │ │ │ │ ├── HCPlayUserInfoModel.swift │ │ │ │ └── HCTrackInfoModel.swift │ │ │ ├── View/ │ │ │ │ ├── Cell/ │ │ │ │ │ ├── HCPlayAlbumCell.swift │ │ │ │ │ ├── HCPlayAlbumCell.xib │ │ │ │ │ ├── HCPlayRecommendCell.swift │ │ │ │ │ ├── HCPlayRecommendCell.xib │ │ │ │ │ ├── HCPlayRecommendFooterCell.swift │ │ │ │ │ ├── HCPlayRecommendFooterCell.xib │ │ │ │ │ ├── HCPlayRecommendHeaderCell.swift │ │ │ │ │ ├── HCPlayRecommendHeaderCell.xib │ │ │ │ │ ├── HCPlaySynopsisCell.swift │ │ │ │ │ ├── HCPlaySynopsisCell.xib │ │ │ │ │ ├── HCPlayUserInfoCell.swift │ │ │ │ │ └── HCPlayUserInfoCell.xib │ │ │ │ ├── HCPlayHeaderView.swift │ │ │ │ ├── HCPlayHeaderView.xib │ │ │ │ ├── HCPlayProgressView.swift │ │ │ │ ├── HCPlayProgressView.xib │ │ │ │ ├── HCPlayTitleView.swift │ │ │ │ ├── HCPlayTitleView.xib │ │ │ │ └── HCTabbarPlayView.swift │ │ │ └── ViewModel/ │ │ │ └── HCPlayViewModel.swift │ │ └── Other/ │ │ ├── AppDelegate.swift │ │ ├── Base.lproj/ │ │ │ └── Main.storyboard │ │ └── ViewController.swift │ ├── Info.plist │ └── LaunchScreen.storyboard ├── RxXMLY.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata/ │ │ └── sessionCh.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata/ │ └── sessionCh.xcuserdatad/ │ └── xcschemes/ │ ├── RxXMLY.xcscheme │ └── xcschememanagement.plist ├── RxXMLY.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcuserdata/ │ └── sessionCh.xcuserdatad/ │ ├── IDEFindNavigatorScopes.plist │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger/ │ └── Breakpoints_v2.xcbkptlist ├── RxXMLYTests/ │ ├── Info.plist │ └── RxXMLYTests.swift └── RxXMLYUITests/ ├── Info.plist └── RxXMLYUITests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitigonre ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## Build generated build/ DerivedData/ ## Various settings *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata/ ## Other *.moved-aside *.xccheckout *.xcscmblueprint ## Obj-C/Swift specific *.hmap *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build # fastlane # # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots fastlane/test_output ================================================ FILE: Podfile ================================================ # Uncomment the next line to define a global platform for your project # platform :ios, '9.1' target 'RxXMLY' do use_frameworks! # Architecture pod 'ReactorKit' # Networking pod 'Alamofire' pod 'Moya' pod 'Kingfisher' pod 'ObjectMapper' pod 'SwiftyJSON' # Rx pod 'RxSwift', '~> 4.0' pod 'RxCocoa' pod 'RxAlamofire' pod 'RxDataSources', '~> 3.0' pod 'NSObject+Rx' pod 'RxGesture' # UI pod 'SnapKit' pod 'TYCyclePagerView' pod 'TYPagerController' pod 'MJRefresh' # Logging # Misc. pod 'IQKeyboardManagerSwift' pod 'Then' pod 'ReusableKit' pod 'SwiftyColor' # pod 'MLeaksFinder' # 检测内存泄漏 pod 'URLNavigator' pod 'TTRangeSlider' # SDK end ================================================ FILE: Pods/Alamofire/LICENSE ================================================ Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) 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. ================================================ FILE: Pods/Alamofire/README.md ================================================ ![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png) [![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg?branch=master)](https://travis-ci.org/Alamofire/Alamofire) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg)](https://img.shields.io/cocoapods/v/Alamofire.svg) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](https://alamofire.github.io/Alamofire) [![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat)](http://twitter.com/AlamofireSF) [![Gitter](https://badges.gitter.im/Alamofire/Alamofire.svg)](https://gitter.im/Alamofire/Alamofire?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Alamofire is an HTTP networking library written in Swift. - [Features](#features) - [Component Libraries](#component-libraries) - [Requirements](#requirements) - [Migration Guides](#migration-guides) - [Communication](#communication) - [Installation](#installation) - [Usage](#usage) - **Intro -** [Making a Request](#making-a-request), [Response Handling](#response-handling), [Response Validation](#response-validation), [Response Caching](#response-caching) - **HTTP -** [HTTP Methods](#http-methods), [Parameter Encoding](#parameter-encoding), [HTTP Headers](#http-headers), [Authentication](#authentication) - **Large Data -** [Downloading Data to a File](#downloading-data-to-a-file), [Uploading Data to a Server](#uploading-data-to-a-server) - **Tools -** [Statistical Metrics](#statistical-metrics), [cURL Command Output](#curl-command-output) - [Advanced Usage](#advanced-usage) - **URL Session -** [Session Manager](#session-manager), [Session Delegate](#session-delegate), [Request](#request) - **Routing -** [Routing Requests](#routing-requests), [Adapting and Retrying Requests](#adapting-and-retrying-requests) - **Model Objects -** [Custom Response Serialization](#custom-response-serialization) - **Connection -** [Security](#security), [Network Reachability](#network-reachability) - [Open Radars](#open-radars) - [FAQ](#faq) - [Credits](#credits) - [Donations](#donations) - [License](#license) ## Features - [x] Chainable Request / Response Methods - [x] URL / JSON / plist Parameter Encoding - [x] Upload File / Data / Stream / MultipartFormData - [x] Download File using Request or Resume Data - [x] Authentication with URLCredential - [x] HTTP Response Validation - [x] Upload and Download Progress Closures with Progress - [x] cURL Command Output - [x] Dynamically Adapt and Retry Requests - [x] TLS Certificate and Public Key Pinning - [x] Network Reachability - [x] Comprehensive Unit and Integration Test Coverage - [x] [Complete Documentation](https://alamofire.github.io/Alamofire) ## Component Libraries In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) to bring additional functionality to the Alamofire ecosystem. - [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - An image library including image response serializers, `UIImage` and `UIImageView` extensions, custom image filters, an auto-purging in-memory cache and a priority-based image downloading system. - [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator) - Controls the visibility of the network activity indicator on iOS using Alamofire. It contains configurable delay timers to help mitigate flicker and can support `URLSession` instances not managed by Alamofire. ## Requirements - iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ - Xcode 8.3+ - Swift 3.1+ ## Migration Guides - [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md) - [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md) - [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md) ## Communication - If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire). (Tag 'alamofire') - If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire). - If you **found a bug**, open an issue. - If you **have a feature request**, open an issue. - If you **want to contribute**, submit a pull request. ## Installation ### CocoaPods [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: ```bash $ gem install cocoapods ``` > CocoaPods 1.1+ is required to build Alamofire 4.0+. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: ```ruby source 'https://github.com/CocoaPods/Specs.git' platform :ios, '10.0' use_frameworks! target '' do pod 'Alamofire', '~> 4.5' end ``` Then, run the following command: ```bash $ pod install ``` ### Carthage [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. You can install Carthage with [Homebrew](http://brew.sh/) using the following command: ```bash $ brew update $ brew install carthage ``` To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl github "Alamofire/Alamofire" ~> 4.5 ``` Run `carthage update` to build the framework and drag the built `Alamofire.framework` into your Xcode project. ### Swift Package Manager The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. It is in early development, but Alamofire does support its use on supported platforms. Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. ```swift dependencies: [ .Package(url: "https://github.com/Alamofire/Alamofire.git", majorVersion: 4) ] ``` ### Manually If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually. #### Embedded Framework - Open up Terminal, `cd` into your top-level project directory, and run the following command "if" your project is not initialized as a git repository: ```bash $ git init ``` - Add Alamofire as a git [submodule](http://git-scm.com/docs/git-submodule) by running the following command: ```bash $ git submodule add https://github.com/Alamofire/Alamofire.git ``` - Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project. > It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter. - Select the `Alamofire.xcodeproj` in the Project Navigator and verify the deployment target matches that of your application target. - Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar. - In the tab bar at the top of that window, open the "General" panel. - Click on the `+` button under the "Embedded Binaries" section. - You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder. > It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`. - Select the top `Alamofire.framework` for iOS and the bottom one for OS X. > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as either `Alamofire iOS`, `Alamofire macOS`, `Alamofire tvOS` or `Alamofire watchOS`. - And that's it! > The `Alamofire.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device. --- ## Usage ### Making a Request ```swift import Alamofire Alamofire.request("https://httpbin.org/get") ``` ### Response Handling Handling the `Response` of a `Request` made in Alamofire involves chaining a response handler onto the `Request`. ```swift Alamofire.request("https://httpbin.org/get").responseJSON { response in print("Request: \(String(describing: response.request))") // original url request print("Response: \(String(describing: response.response))") // http url response print("Result: \(response.result)") // response serialization result if let json = response.result.value { print("JSON: \(json)") // serialized json response } if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) { print("Data: \(utf8Text)") // original server data as UTF8 string } } ``` In the above example, the `responseJSON` handler is appended to the `Request` to be executed once the `Request` is complete. Rather than blocking execution to wait for a response from the server, a [callback](http://en.wikipedia.org/wiki/Callback_%28computer_programming%29) in the form of a closure is specified to handle the response once it's received. The result of a request is only available inside the scope of a response closure. Any execution contingent on the response or data received from the server must be done within a response closure. > Networking in Alamofire is done _asynchronously_. Asynchronous programming may be a source of frustration to programmers unfamiliar with the concept, but there are [very good reasons](https://developer.apple.com/library/ios/qa/qa1693/_index.html) for doing it this way. Alamofire contains five different response handlers by default including: ```swift // Response Handler - Unserialized Response func response( queue: DispatchQueue?, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self // Response Data Handler - Serialized into Data func responseData( queue: DispatchQueue?, completionHandler: @escaping (DataResponse) -> Void) -> Self // Response String Handler - Serialized into String func responseString( queue: DispatchQueue?, encoding: String.Encoding?, completionHandler: @escaping (DataResponse) -> Void) -> Self // Response JSON Handler - Serialized into Any func responseJSON( queue: DispatchQueue?, completionHandler: @escaping (DataResponse) -> Void) -> Self // Response PropertyList (plist) Handler - Serialized into Any func responsePropertyList( queue: DispatchQueue?, completionHandler: @escaping (DataResponse) -> Void)) -> Self ``` None of the response handlers perform any validation of the `HTTPURLResponse` it gets back from the server. > For example, response status codes in the `400..<500` and `500..<600` ranges do NOT automatically trigger an `Error`. Alamofire uses [Response Validation](#response-validation) method chaining to achieve this. #### Response Handler The `response` handler does NOT evaluate any of the response data. It merely forwards on all information directly from the URL session delegate. It is the Alamofire equivalent of using `cURL` to execute a `Request`. ```swift Alamofire.request("https://httpbin.org/get").response { response in print("Request: \(response.request)") print("Response: \(response.response)") print("Error: \(response.error)") if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) { print("Data: \(utf8Text)") } } ``` > We strongly encourage you to leverage the other response serializers taking advantage of `Response` and `Result` types. #### Response Data Handler The `responseData` handler uses the `responseDataSerializer` (the object that serializes the server data into some other type) to extract the `Data` returned by the server. If no errors occur and `Data` is returned, the response `Result` will be a `.success` and the `value` will be of type `Data`. ```swift Alamofire.request("https://httpbin.org/get").responseData { response in debugPrint("All Response Info: \(response)") if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) { print("Data: \(utf8Text)") } } ``` #### Response String Handler The `responseString` handler uses the `responseStringSerializer` to convert the `Data` returned by the server into a `String` with the specified encoding. If no errors occur and the server data is successfully serialized into a `String`, the response `Result` will be a `.success` and the `value` will be of type `String`. ```swift Alamofire.request("https://httpbin.org/get").responseString { response in print("Success: \(response.result.isSuccess)") print("Response String: \(response.result.value)") } ``` > If no encoding is specified, Alamofire will use the text encoding specified in the `HTTPURLResponse` from the server. If the text encoding cannot be determined by the server response, it defaults to `.isoLatin1`. #### Response JSON Handler The `responseJSON` handler uses the `responseJSONSerializer` to convert the `Data` returned by the server into an `Any` type using the specified `JSONSerialization.ReadingOptions`. If no errors occur and the server data is successfully serialized into a JSON object, the response `Result` will be a `.success` and the `value` will be of type `Any`. ```swift Alamofire.request("https://httpbin.org/get").responseJSON { response in debugPrint(response) if let json = response.result.value { print("JSON: \(json)") } } ``` > All JSON serialization is handled by the `JSONSerialization` API in the `Foundation` framework. #### Chained Response Handlers Response handlers can even be chained: ```swift Alamofire.request("https://httpbin.org/get") .responseString { response in print("Response String: \(response.result.value)") } .responseJSON { response in print("Response JSON: \(response.result.value)") } ``` > It is important to note that using multiple response handlers on the same `Request` requires the server data to be serialized multiple times. Once for each response handler. #### Response Handler Queue Response handlers by default are executed on the main dispatch queue. However, a custom dispatch queue can be provided instead. ```swift let utilityQueue = DispatchQueue.global(qos: .utility) Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in print("Executing response handler on utility queue") } ``` ### Response Validation By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling `validate` before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type. #### Manual Validation ```swift Alamofire.request("https://httpbin.org/get") .validate(statusCode: 200..<300) .validate(contentType: ["application/json"]) .responseData { response in switch response.result { case .success: print("Validation Successful") case .failure(let error): print(error) } } ``` #### Automatic Validation Automatically validates status code within `200..<300` range, and that the `Content-Type` header of the response matches the `Accept` header of the request, if one is provided. ```swift Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in switch response.result { case .success: print("Validation Successful") case .failure(let error): print(error) } } ``` ### Response Caching Response Caching is handled on the system framework level by [`URLCache`](https://developer.apple.com/reference/foundation/urlcache). It provides a composite in-memory and on-disk cache and lets you manipulate the sizes of both the in-memory and on-disk portions. > By default, Alamofire leverages the shared `URLCache`. In order to customize it, see the [Session Manager Configurations](#session-manager) section. ### HTTP Methods The `HTTPMethod` enumeration lists the HTTP methods defined in [RFC 7231 §4.3](http://tools.ietf.org/html/rfc7231#section-4.3): ```swift public enum HTTPMethod: String { case options = "OPTIONS" case get = "GET" case head = "HEAD" case post = "POST" case put = "PUT" case patch = "PATCH" case delete = "DELETE" case trace = "TRACE" case connect = "CONNECT" } ``` These values can be passed as the `method` argument to the `Alamofire.request` API: ```swift Alamofire.request("https://httpbin.org/get") // method defaults to `.get` Alamofire.request("https://httpbin.org/post", method: .post) Alamofire.request("https://httpbin.org/put", method: .put) Alamofire.request("https://httpbin.org/delete", method: .delete) ``` > The `Alamofire.request` method parameter defaults to `.get`. ### Parameter Encoding Alamofire supports three types of parameter encoding including: `URL`, `JSON` and `PropertyList`. It can also support any custom encoding that conforms to the `ParameterEncoding` protocol. #### URL Encoding The `URLEncoding` type creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP body of the URL request. Whether the query string is set or appended to any existing URL query string or set as the HTTP body depends on the `Destination` of the encoding. The `Destination` enumeration has three cases: - `.methodDependent` - Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` requests and sets as the HTTP body for requests with any other HTTP method. - `.queryString` - Sets or appends encoded query string result to existing query string. - `.httpBody` - Sets encoded query string result as the HTTP body of the URL request. The `Content-Type` HTTP header field of an encoded request with HTTP body is set to `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification for how to encode collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`). ##### GET Request With URL-Encoded Parameters ```swift let parameters: Parameters = ["foo": "bar"] // All three of these calls are equivalent Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default` Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default) Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent)) // https://httpbin.org/get?foo=bar ``` ##### POST Request With URL-Encoded Parameters ```swift let parameters: Parameters = [ "foo": "bar", "baz": ["a", 1], "qux": [ "x": 1, "y": 2, "z": 3 ] ] // All three of these calls are equivalent Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters) Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default) Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody) // HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3 ``` #### JSON Encoding The `JSONEncoding` type creates a JSON representation of the parameters object, which is set as the HTTP body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. ##### POST Request with JSON-Encoded Parameters ```swift let parameters: Parameters = [ "foo": [1,2,3], "bar": [ "baz": "qux" ] ] // Both calls are equivalent Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default) Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: [])) // HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}} ``` #### Property List Encoding The `PropertyListEncoding` uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/x-plist`. #### Custom Encoding In the event that the provided `ParameterEncoding` types do not meet your needs, you can create your own custom encoding. Here's a quick example of how you could build a custom `JSONStringArrayEncoding` type to encode a JSON string array onto a `Request`. ```swift struct JSONStringArrayEncoding: ParameterEncoding { private let array: [String] init(array: [String]) { self.array = array } func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { var urlRequest = try urlRequest.asURLRequest() let data = try JSONSerialization.data(withJSONObject: array, options: []) if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") } urlRequest.httpBody = data return urlRequest } } ``` #### Manual Parameter Encoding of a URLRequest The `ParameterEncoding` APIs can be used outside of making network requests. ```swift let url = URL(string: "https://httpbin.org/get")! var urlRequest = URLRequest(url: url) let parameters: Parameters = ["foo": "bar"] let encodedURLRequest = try URLEncoding.queryString.encode(urlRequest, with: parameters) ``` ### HTTP Headers Adding a custom HTTP header to a `Request` is supported directly in the global `request` method. This makes it easy to attach HTTP headers to a `Request` that can be constantly changing. ```swift let headers: HTTPHeaders = [ "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", "Accept": "application/json" ] Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in debugPrint(response) } ``` > For HTTP headers that do not change, it is recommended to set them on the `URLSessionConfiguration` so they are automatically applied to any `URLSessionTask` created by the underlying `URLSession`. For more information, see the [Session Manager Configurations](#session-manager) section. The default Alamofire `SessionManager` provides a default set of headers for every `Request`. These include: - `Accept-Encoding`, which defaults to `gzip;q=1.0, compress;q=0.5`, per [RFC 7230 §4.2.3](https://tools.ietf.org/html/rfc7230#section-4.2.3). - `Accept-Language`, which defaults to up to the top 6 preferred languages on the system, formatted like `en;q=1.0`, per [RFC 7231 §5.3.5](https://tools.ietf.org/html/rfc7231#section-5.3.5). - `User-Agent`, which contains versioning information about the current app. For example: `iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`, per [RFC 7231 §5.5.3](https://tools.ietf.org/html/rfc7231#section-5.5.3). If you need to customize these headers, a custom `URLSessionConfiguration` should be created, the `defaultHTTPHeaders` property updated and the configuration applied to a new `SessionManager` instance. ### Authentication Authentication is handled on the system framework level by [`URLCredential`](https://developer.apple.com/reference/foundation/nsurlcredential) and [`URLAuthenticationChallenge`](https://developer.apple.com/reference/foundation/urlauthenticationchallenge). **Supported Authentication Schemes** - [HTTP Basic](http://en.wikipedia.org/wiki/Basic_access_authentication) - [HTTP Digest](http://en.wikipedia.org/wiki/Digest_access_authentication) - [Kerberos](http://en.wikipedia.org/wiki/Kerberos_%28protocol%29) - [NTLM](http://en.wikipedia.org/wiki/NT_LAN_Manager) #### HTTP Basic Authentication The `authenticate` method on a `Request` will automatically provide a `URLCredential` to a `URLAuthenticationChallenge` when appropriate: ```swift let user = "user" let password = "password" Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)") .authenticate(user: user, password: password) .responseJSON { response in debugPrint(response) } ``` Depending upon your server implementation, an `Authorization` header may also be appropriate: ```swift let user = "user" let password = "password" var headers: HTTPHeaders = [:] if let authorizationHeader = Request.authorizationHeader(user: user, password: password) { headers[authorizationHeader.key] = authorizationHeader.value } Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers) .responseJSON { response in debugPrint(response) } ``` #### Authentication with URLCredential ```swift let user = "user" let password = "password" let credential = URLCredential(user: user, password: password, persistence: .forSession) Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)") .authenticate(usingCredential: credential) .responseJSON { response in debugPrint(response) } ``` > It is important to note that when using a `URLCredential` for authentication, the underlying `URLSession` will actually end up making two requests if a challenge is issued by the server. The first request will not include the credential which "may" trigger a challenge from the server. The challenge is then received by Alamofire, the credential is appended and the request is retried by the underlying `URLSession`. ### Downloading Data to a File Requests made in Alamofire that fetch data from a server can download the data in-memory or on-disk. The `Alamofire.request` APIs used in all the examples so far always downloads the server data in-memory. This is great for smaller payloads because it's more efficient, but really bad for larger payloads because the download could run your entire application out-of-memory. Because of this, you can also use the `Alamofire.download` APIs to download the server data to a temporary file on-disk. > This will only work on `macOS` as is. Other platforms don't allow access to the filesystem outside of your app's sandbox. To download files on other platforms, see the [Download File Destination](#download-file-destination) section. ```swift Alamofire.download("https://httpbin.org/image/png").responseData { response in if let data = response.result.value { let image = UIImage(data: data) } } ``` > The `Alamofire.download` APIs should also be used if you need to download data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager) section. #### Download File Destination You can also provide a `DownloadFileDestination` closure to move the file from the temporary directory to a final destination. Before the temporary file is actually moved to the `destinationURL`, the `DownloadOptions` specified in the closure will be executed. The two currently supported `DownloadOptions` are: - `.createIntermediateDirectories` - Creates intermediate directories for the destination URL if specified. - `.removePreviousFile` - Removes a previous file from the destination URL if specified. ```swift let destination: DownloadRequest.DownloadFileDestination = { _, _ in let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let fileURL = documentsURL.appendingPathComponent("pig.png") return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) } Alamofire.download(urlString, to: destination).response { response in print(response) if response.error == nil, let imagePath = response.destinationURL?.path { let image = UIImage(contentsOfFile: imagePath) } } ``` You can also use the suggested download destination API. ```swift let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory) Alamofire.download("https://httpbin.org/image/png", to: destination) ``` #### Download Progress Many times it can be helpful to report download progress to the user. Any `DownloadRequest` can report download progress using the `downloadProgress` API. ```swift Alamofire.download("https://httpbin.org/image/png") .downloadProgress { progress in print("Download Progress: \(progress.fractionCompleted)") } .responseData { response in if let data = response.result.value { let image = UIImage(data: data) } } ``` The `downloadProgress` API also takes a `queue` parameter which defines which `DispatchQueue` the download progress closure should be called on. ```swift let utilityQueue = DispatchQueue.global(qos: .utility) Alamofire.download("https://httpbin.org/image/png") .downloadProgress(queue: utilityQueue) { progress in print("Download Progress: \(progress.fractionCompleted)") } .responseData { response in if let data = response.result.value { let image = UIImage(data: data) } } ``` #### Resuming a Download If a `DownloadRequest` is cancelled or interrupted, the underlying URL session may generate resume data for the active `DownloadRequest`. If this happens, the resume data can be re-used to restart the `DownloadRequest` where it left off. The resume data can be accessed through the download response, then reused when trying to restart the request. > **IMPORTANT:** On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the data is written incorrectly and will always fail to resume the download. For more information about the bug and possible workarounds, please see this Stack Overflow [post](http://stackoverflow.com/a/39347461/1342462). ```swift class ImageRequestor { private var resumeData: Data? private var image: UIImage? func fetchImage(completion: (UIImage?) -> Void) { guard image == nil else { completion(image) ; return } let destination: DownloadRequest.DownloadFileDestination = { _, _ in let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let fileURL = documentsURL.appendingPathComponent("pig.png") return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) } let request: DownloadRequest if let resumeData = resumeData { request = Alamofire.download(resumingWith: resumeData) } else { request = Alamofire.download("https://httpbin.org/image/png") } request.responseData { response in switch response.result { case .success(let data): self.image = UIImage(data: data) case .failure: self.resumeData = response.resumeData } } } } ``` ### Uploading Data to a Server When sending relatively small amounts of data to a server using JSON or URL encoded parameters, the `Alamofire.request` APIs are usually sufficient. If you need to send much larger amounts of data from a file URL or an `InputStream`, then the `Alamofire.upload` APIs are what you want to use. > The `Alamofire.upload` APIs should also be used if you need to upload data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager) section. #### Uploading Data ```swift let imageData = UIImagePNGRepresentation(image)! Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in debugPrint(response) } ``` #### Uploading a File ```swift let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov") Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in debugPrint(response) } ``` #### Uploading Multipart Form Data ```swift Alamofire.upload( multipartFormData: { multipartFormData in multipartFormData.append(unicornImageURL, withName: "unicorn") multipartFormData.append(rainbowImageURL, withName: "rainbow") }, to: "https://httpbin.org/post", encodingCompletion: { encodingResult in switch encodingResult { case .success(let upload, _, _): upload.responseJSON { response in debugPrint(response) } case .failure(let encodingError): print(encodingError) } } ) ``` #### Upload Progress While your user is waiting for their upload to complete, sometimes it can be handy to show the progress of the upload to the user. Any `UploadRequest` can report both upload progress and download progress of the response data using the `uploadProgress` and `downloadProgress` APIs. ```swift let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov") Alamofire.upload(fileURL, to: "https://httpbin.org/post") .uploadProgress { progress in // main queue by default print("Upload Progress: \(progress.fractionCompleted)") } .downloadProgress { progress in // main queue by default print("Download Progress: \(progress.fractionCompleted)") } .responseJSON { response in debugPrint(response) } ``` ### Statistical Metrics #### Timeline Alamofire collects timings throughout the lifecycle of a `Request` and creates a `Timeline` object exposed as a property on all response types. ```swift Alamofire.request("https://httpbin.org/get").responseJSON { response in print(response.timeline) } ``` The above reports the following `Timeline` info: - `Latency`: 0.428 seconds - `Request Duration`: 0.428 seconds - `Serialization Duration`: 0.001 seconds - `Total Duration`: 0.429 seconds #### URL Session Task Metrics In iOS and tvOS 10 and macOS 10.12, Apple introduced the new [URLSessionTaskMetrics](https://developer.apple.com/reference/foundation/urlsessiontaskmetrics) APIs. The task metrics encapsulate some fantastic statistical information about the request and response execution. The API is very similar to the `Timeline`, but provides many more statistics that Alamofire doesn't have access to compute. The metrics can be accessed through any response type. ```swift Alamofire.request("https://httpbin.org/get").responseJSON { response in print(response.metrics) } ``` It's important to note that these APIs are only available on iOS and tvOS 10 and macOS 10.12. Therefore, depending on your deployment target, you may need to use these inside availability checks: ```swift Alamofire.request("https://httpbin.org/get").responseJSON { response in if #available(iOS 10.0, *) { print(response.metrics) } } ``` ### cURL Command Output Debugging platform issues can be frustrating. Thankfully, Alamofire `Request` objects conform to both the `CustomStringConvertible` and `CustomDebugStringConvertible` protocols to provide some VERY helpful debugging tools. #### CustomStringConvertible ```swift let request = Alamofire.request("https://httpbin.org/ip") print(request) // GET https://httpbin.org/ip (200) ``` #### CustomDebugStringConvertible ```swift let request = Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]) debugPrint(request) ``` Outputs: ```bash $ curl -i \ -H "User-Agent: Alamofire/4.0.0" \ -H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \ -H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \ "https://httpbin.org/get?foo=bar" ``` --- ## Advanced Usage Alamofire is built on `URLSession` and the Foundation URL Loading System. To make the most of this framework, it is recommended that you be familiar with the concepts and capabilities of the underlying networking stack. **Recommended Reading** - [URL Loading System Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html) - [URLSession Class Reference](https://developer.apple.com/reference/foundation/nsurlsession) - [URLCache Class Reference](https://developer.apple.com/reference/foundation/urlcache) - [URLAuthenticationChallenge Class Reference](https://developer.apple.com/reference/foundation/urlauthenticationchallenge) ### Session Manager Top-level convenience methods like `Alamofire.request` use a default instance of `Alamofire.SessionManager`, which is configured with the default `URLSessionConfiguration`. As such, the following two statements are equivalent: ```swift Alamofire.request("https://httpbin.org/get") ``` ```swift let sessionManager = Alamofire.SessionManager.default sessionManager.request("https://httpbin.org/get") ``` Applications can create session managers for background and ephemeral sessions, as well as new managers that customize the default session configuration, such as for default headers (`httpAdditionalHeaders`) or timeout interval (`timeoutIntervalForRequest`). #### Creating a Session Manager with Default Configuration ```swift let configuration = URLSessionConfiguration.default let sessionManager = Alamofire.SessionManager(configuration: configuration) ``` #### Creating a Session Manager with Background Configuration ```swift let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background") let sessionManager = Alamofire.SessionManager(configuration: configuration) ``` #### Creating a Session Manager with Ephemeral Configuration ```swift let configuration = URLSessionConfiguration.ephemeral let sessionManager = Alamofire.SessionManager(configuration: configuration) ``` #### Modifying the Session Configuration ```swift var defaultHeaders = Alamofire.SessionManager.defaultHTTPHeaders defaultHeaders["DNT"] = "1 (Do Not Track Enabled)" let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = defaultHeaders let sessionManager = Alamofire.SessionManager(configuration: configuration) ``` > This is **not** recommended for `Authorization` or `Content-Type` headers. Instead, use the `headers` parameter in the top-level `Alamofire.request` APIs, `URLRequestConvertible` and `ParameterEncoding`, respectively. ### Session Delegate By default, an Alamofire `SessionManager` instance creates a `SessionDelegate` object to handle all the various types of delegate callbacks that are generated by the underlying `URLSession`. The implementations of each delegate method handle the most common use cases for these types of calls abstracting the complexity away from the top-level APIs. However, advanced users may find the need to override the default functionality for various reasons. #### Override Closures The first way to customize the `SessionDelegate` behavior is through the use of the override closures. Each closure gives you the ability to override the implementation of the matching `SessionDelegate` API, yet still use the default implementation for all other APIs. This makes it easy to customize subsets of the delegate functionality. Here are a few examples of some of the override closures available: ```swift /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`. open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`. open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`. open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`. open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? ``` The following is a short example of how to use the `taskWillPerformHTTPRedirection` to avoid following redirects to any `apple.com` domains. ```swift let sessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.default) let delegate: Alamofire.SessionDelegate = sessionManager.delegate delegate.taskWillPerformHTTPRedirection = { session, task, response, request in var finalRequest = request if let originalRequest = task.originalRequest, let urlString = originalRequest.url?.urlString, urlString.contains("apple.com") { finalRequest = originalRequest } return finalRequest } ``` #### Subclassing Another way to override the default implementation of the `SessionDelegate` is to subclass it. Subclassing allows you completely customize the behavior of the API or to create a proxy for the API and still use the default implementation. Creating a proxy allows you to log events, emit notifications, provide pre and post hook implementations, etc. Here's a quick example of subclassing the `SessionDelegate` and logging a message when a redirect occurs. ```swift class LoggingSessionDelegate: SessionDelegate { override func urlSession( _ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { print("URLSession will perform HTTP redirection to request: \(request)") super.urlSession( session, task: task, willPerformHTTPRedirection: response, newRequest: request, completionHandler: completionHandler ) } } ``` Generally speaking, either the default implementation or the override closures should provide the necessary functionality required. Subclassing should only be used as a last resort. > It is important to keep in mind that the `subdelegates` are initialized and destroyed in the default implementation. Be careful when subclassing to not introduce memory leaks. ### Request The result of a `request`, `download`, `upload` or `stream` methods are a `DataRequest`, `DownloadRequest`, `UploadRequest` and `StreamRequest` which all inherit from `Request`. All `Request` instances are always created by an owning session manager, and never initialized directly. Each subclass has specialized methods such as `authenticate`, `validate`, `responseJSON` and `uploadProgress` that each return the caller instance in order to facilitate method chaining. Requests can be suspended, resumed and cancelled: - `suspend()`: Suspends the underlying task and dispatch queue. - `resume()`: Resumes the underlying task and dispatch queue. If the owning manager does not have `startRequestsImmediately` set to `true`, the request must call `resume()` in order to start. - `cancel()`: Cancels the underlying task, producing an error that is passed to any registered response handlers. ### Routing Requests As apps grow in size, it's important to adopt common patterns as you build out your network stack. An important part of that design is how to route your requests. The Alamofire `URLConvertible` and `URLRequestConvertible` protocols along with the `Router` design pattern are here to help. #### URLConvertible Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct URL requests internally. `String`, `URL`, and `URLComponents` conform to `URLConvertible` by default, allowing any of them to be passed as `url` parameters to the `request`, `upload`, and `download` methods: ```swift let urlString = "https://httpbin.org/post" Alamofire.request(urlString, method: .post) let url = URL(string: urlString)! Alamofire.request(url, method: .post) let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true)! Alamofire.request(urlComponents, method: .post) ``` Applications interacting with web applications in a significant manner are encouraged to have custom types conform to `URLConvertible` as a convenient way to map domain-specific models to server resources. ##### Type-Safe Routing ```swift extension User: URLConvertible { static let baseURLString = "https://example.com" func asURL() throws -> URL { let urlString = User.baseURLString + "/users/\(username)/" return try urlString.asURL() } } ``` ```swift let user = User(username: "mattt") Alamofire.request(user) // https://example.com/users/mattt ``` #### URLRequestConvertible Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. `URLRequest` conforms to `URLRequestConvertible` by default, allowing it to be passed into `request`, `upload`, and `download` methods directly (this is the recommended way to specify custom HTTP body for individual requests): ```swift let url = URL(string: "https://httpbin.org/post")! var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "POST" let parameters = ["foo": "bar"] do { urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: []) } catch { // No-op } urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") Alamofire.request(urlRequest) ``` Applications interacting with web applications in a significant manner are encouraged to have custom types conform to `URLRequestConvertible` as a way to ensure consistency of requested endpoints. Such an approach can be used to abstract away server-side inconsistencies and provide type-safe routing, as well as manage authentication credentials and other state. ##### API Parameter Abstraction ```swift enum Router: URLRequestConvertible { case search(query: String, page: Int) static let baseURLString = "https://example.com" static let perPage = 50 // MARK: URLRequestConvertible func asURLRequest() throws -> URLRequest { let result: (path: String, parameters: Parameters) = { switch self { case let .search(query, page) where page > 0: return ("/search", ["q": query, "offset": Router.perPage * page]) case let .search(query, _): return ("/search", ["q": query]) } }() let url = try Router.baseURLString.asURL() let urlRequest = URLRequest(url: url.appendingPathComponent(result.path)) return try URLEncoding.default.encode(urlRequest, with: result.parameters) } } ``` ```swift Alamofire.request(Router.search(query: "foo bar", page: 1)) // https://example.com/search?q=foo%20bar&offset=50 ``` ##### CRUD & Authorization ```swift import Alamofire enum Router: URLRequestConvertible { case createUser(parameters: Parameters) case readUser(username: String) case updateUser(username: String, parameters: Parameters) case destroyUser(username: String) static let baseURLString = "https://example.com" var method: HTTPMethod { switch self { case .createUser: return .post case .readUser: return .get case .updateUser: return .put case .destroyUser: return .delete } } var path: String { switch self { case .createUser: return "/users" case .readUser(let username): return "/users/\(username)" case .updateUser(let username, _): return "/users/\(username)" case .destroyUser(let username): return "/users/\(username)" } } // MARK: URLRequestConvertible func asURLRequest() throws -> URLRequest { let url = try Router.baseURLString.asURL() var urlRequest = URLRequest(url: url.appendingPathComponent(path)) urlRequest.httpMethod = method.rawValue switch self { case .createUser(let parameters): urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters) case .updateUser(_, let parameters): urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters) default: break } return urlRequest } } ``` ```swift Alamofire.request(Router.readUser("mattt")) // GET https://example.com/users/mattt ``` ### Adapting and Retrying Requests Most web services these days are behind some sort of authentication system. One of the more common ones today is OAuth. This generally involves generating an access token authorizing your application or user to call the various supported web services. While creating these initial access tokens can be laborsome, it can be even more complicated when your access token expires and you need to fetch a new one. There are many thread-safety issues that need to be considered. The `RequestAdapter` and `RequestRetrier` protocols were created to make it much easier to create a thread-safe authentication system for a specific set of web services. #### RequestAdapter The `RequestAdapter` protocol allows each `Request` made on a `SessionManager` to be inspected and adapted before being created. One very specific way to use an adapter is to append an `Authorization` header to requests behind a certain type of authentication. ```swift class AccessTokenAdapter: RequestAdapter { private let accessToken: String init(accessToken: String) { self.accessToken = accessToken } func adapt(_ urlRequest: URLRequest) throws -> URLRequest { var urlRequest = urlRequest if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix("https://httpbin.org") { urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") } return urlRequest } } ``` ```swift let sessionManager = SessionManager() sessionManager.adapter = AccessTokenAdapter(accessToken: "1234") sessionManager.request("https://httpbin.org/get") ``` #### RequestRetrier The `RequestRetrier` protocol allows a `Request` that encountered an `Error` while being executed to be retried. When using both the `RequestAdapter` and `RequestRetrier` protocols together, you can create credential refresh systems for OAuth1, OAuth2, Basic Auth and even exponential backoff retry policies. The possibilities are endless. Here's an example of how you could implement a refresh flow for OAuth2 access tokens. > **DISCLAIMER:** This is **NOT** a global `OAuth2` solution. It is merely an example demonstrating how one could use the `RequestAdapter` in conjunction with the `RequestRetrier` to create a thread-safe refresh system. > To reiterate, **do NOT copy** this sample code and drop it into a production application. This is merely an example. Each authentication system must be tailored to a particular platform and authentication type. ```swift class OAuth2Handler: RequestAdapter, RequestRetrier { private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?, _ refreshToken: String?) -> Void private let sessionManager: SessionManager = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders return SessionManager(configuration: configuration) }() private let lock = NSLock() private var clientID: String private var baseURLString: String private var accessToken: String private var refreshToken: String private var isRefreshing = false private var requestsToRetry: [RequestRetryCompletion] = [] // MARK: - Initialization public init(clientID: String, baseURLString: String, accessToken: String, refreshToken: String) { self.clientID = clientID self.baseURLString = baseURLString self.accessToken = accessToken self.refreshToken = refreshToken } // MARK: - RequestAdapter func adapt(_ urlRequest: URLRequest) throws -> URLRequest { if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) { var urlRequest = urlRequest urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") return urlRequest } return urlRequest } // MARK: - RequestRetrier func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) { lock.lock() ; defer { lock.unlock() } if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 { requestsToRetry.append(completion) if !isRefreshing { refreshTokens { [weak self] succeeded, accessToken, refreshToken in guard let strongSelf = self else { return } strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() } if let accessToken = accessToken, let refreshToken = refreshToken { strongSelf.accessToken = accessToken strongSelf.refreshToken = refreshToken } strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) } strongSelf.requestsToRetry.removeAll() } } } else { completion(false, 0.0) } } // MARK: - Private - Refresh Tokens private func refreshTokens(completion: @escaping RefreshCompletion) { guard !isRefreshing else { return } isRefreshing = true let urlString = "\(baseURLString)/oauth2/token" let parameters: [String: Any] = [ "access_token": accessToken, "refresh_token": refreshToken, "client_id": clientID, "grant_type": "refresh_token" ] sessionManager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default) .responseJSON { [weak self] response in guard let strongSelf = self else { return } if let json = response.result.value as? [String: Any], let accessToken = json["access_token"] as? String, let refreshToken = json["refresh_token"] as? String { completion(true, accessToken, refreshToken) } else { completion(false, nil, nil) } strongSelf.isRefreshing = false } } } ``` ```swift let baseURLString = "https://some.domain-behind-oauth2.com" let oauthHandler = OAuth2Handler( clientID: "12345678", baseURLString: baseURLString, accessToken: "abcd1234", refreshToken: "ef56789a" ) let sessionManager = SessionManager() sessionManager.adapter = oauthHandler sessionManager.retrier = oauthHandler let urlString = "\(baseURLString)/some/endpoint" sessionManager.request(urlString).validate().responseJSON { response in debugPrint(response) } ``` Once the `OAuth2Handler` is applied as both the `adapter` and `retrier` for the `SessionManager`, it will handle an invalid access token error by automatically refreshing the access token and retrying all failed requests in the same order they failed. > If you needed them to execute in the same order they were created, you could sort them by their task identifiers. The example above only checks for a `401` response code which is not nearly robust enough, but does demonstrate how one could check for an invalid access token error. In a production application, one would want to check the `realm` and most likely the `www-authenticate` header response although it depends on the OAuth2 implementation. Another important note is that this authentication system could be shared between multiple session managers. For example, you may need to use both a `default` and `ephemeral` session configuration for the same set of web services. The example above allows the same `oauthHandler` instance to be shared across multiple session managers to manage the single refresh flow. ### Custom Response Serialization Alamofire provides built-in response serialization for data, strings, JSON, and property lists: ```swift Alamofire.request(...).responseData { (resp: DataResponse) in ... } Alamofire.request(...).responseString { (resp: DataResponse) in ... } Alamofire.request(...).responseJSON { (resp: DataResponse) in ... } Alamofire.request(...).responsePropertyList { resp: DataResponse) in ... } ``` Those responses wrap deserialized *values* (Data, String, Any) or *errors* (network, validation errors), as well as *meta-data* (URL request, HTTP headers, status code, [metrics](#statistical-metrics), ...). You have several ways to customize all of those response elements: - [Response Mapping](#response-mapping) - [Handling Errors](#handling-errors) - [Creating a Custom Response Serializer](#creating-a-custom-response-serializer) - [Generic Response Object Serialization](#generic-response-object-serialization) #### Response Mapping Response mapping is the simplest way to produce customized responses. It transforms the value of a response, while preserving eventual errors and meta-data. For example, you can turn a json response `DataResponse` into a response that holds an application model, such as `DataResponse`. You perform response mapping with the `DataResponse.map` method: ```swift Alamofire.request("https://example.com/users/mattt").responseJSON { (response: DataResponse) in let userResponse = response.map { json in // We assume an existing User(json: Any) initializer return User(json: json) } // Process userResponse, of type DataResponse: if let user = userResponse.value { print("User: { username: \(user.username), name: \(user.name) }") } } ``` When the transformation may throw an error, use `flatMap` instead: ```swift Alamofire.request("https://example.com/users/mattt").responseJSON { response in let userResponse = response.flatMap { json in try User(json: json) } } ``` Response mapping is a good fit for your custom completion handlers: ```swift @discardableResult func loadUser(completionHandler: @escaping (DataResponse) -> Void) -> Alamofire.DataRequest { return Alamofire.request("https://example.com/users/mattt").responseJSON { response in let userResponse = response.flatMap { json in try User(json: json) } completionHandler(userResponse) } } loadUser { response in if let user = response.value { print("User: { username: \(user.username), name: \(user.name) }") } } ``` When the map/flatMap closure may process a big amount of data, make sure you execute it outside of the main thread: ```swift @discardableResult func loadUser(completionHandler: @escaping (DataResponse) -> Void) -> Alamofire.DataRequest { let utilityQueue = DispatchQueue.global(qos: .utility) return Alamofire.request("https://example.com/users/mattt").responseJSON(queue: utilityQueue) { response in let userResponse = response.flatMap { json in try User(json: json) } DispatchQueue.main.async { completionHandler(userResponse) } } } ``` `map` and `flatMap` are also available for [download responses](#downloading-data-to-a-file). #### Handling Errors Before implementing custom response serializers or object serialization methods, it's important to consider how to handle any errors that may occur. There are two basic options: passing existing errors along unmodified, to be dealt with at response time; or, wrapping all errors in an `Error` type specific to your app. For example, here's a simple `BackendError` enum which will be used in later examples: ```swift enum BackendError: Error { case network(error: Error) // Capture any underlying Error from the URLSession API case dataSerialization(error: Error) case jsonSerialization(error: Error) case xmlSerialization(error: Error) case objectSerialization(reason: String) } ``` #### Creating a Custom Response Serializer Alamofire provides built-in response serialization for strings, JSON, and property lists, but others can be added in extensions on `Alamofire.DataRequest` and / or `Alamofire.DownloadRequest`. For example, here's how a response handler using [Ono](https://github.com/mattt/Ono) might be implemented: ```swift extension DataRequest { static func xmlResponseSerializer() -> DataResponseSerializer { return DataResponseSerializer { request, response, data, error in // Pass through any underlying URLSession error to the .network case. guard error == nil else { return .failure(BackendError.network(error: error!)) } // Use Alamofire's existing data serializer to extract the data, passing the error as nil, as it has // already been handled. let result = Request.serializeResponseData(response: response, data: data, error: nil) guard case let .success(validData) = result else { return .failure(BackendError.dataSerialization(error: result.error! as! AFError)) } do { let xml = try ONOXMLDocument(data: validData) return .success(xml) } catch { return .failure(BackendError.xmlSerialization(error: error)) } } } @discardableResult func responseXMLDocument( queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.xmlResponseSerializer(), completionHandler: completionHandler ) } } ``` #### Generic Response Object Serialization Generics can be used to provide automatic, type-safe response object serialization. ```swift protocol ResponseObjectSerializable { init?(response: HTTPURLResponse, representation: Any) } extension DataRequest { func responseObject( queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse) -> Void) -> Self { let responseSerializer = DataResponseSerializer { request, response, data, error in guard error == nil else { return .failure(BackendError.network(error: error!)) } let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) let result = jsonResponseSerializer.serializeResponse(request, response, data, nil) guard case let .success(jsonObject) = result else { return .failure(BackendError.jsonSerialization(error: result.error!)) } guard let response = response, let responseObject = T(response: response, representation: jsonObject) else { return .failure(BackendError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)")) } return .success(responseObject) } return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) } } ``` ```swift struct User: ResponseObjectSerializable, CustomStringConvertible { let username: String let name: String var description: String { return "User: { username: \(username), name: \(name) }" } init?(response: HTTPURLResponse, representation: Any) { guard let username = response.url?.lastPathComponent, let representation = representation as? [String: Any], let name = representation["name"] as? String else { return nil } self.username = username self.name = name } } ``` ```swift Alamofire.request("https://example.com/users/mattt").responseObject { (response: DataResponse) in debugPrint(response) if let user = response.result.value { print("User: { username: \(user.username), name: \(user.name) }") } } ``` The same approach can also be used to handle endpoints that return a representation of a collection of objects: ```swift protocol ResponseCollectionSerializable { static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] } extension ResponseCollectionSerializable where Self: ResponseObjectSerializable { static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] { var collection: [Self] = [] if let representation = representation as? [[String: Any]] { for itemRepresentation in representation { if let item = Self(response: response, representation: itemRepresentation) { collection.append(item) } } } return collection } } ``` ```swift extension DataRequest { @discardableResult func responseCollection( queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self { let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in guard error == nil else { return .failure(BackendError.network(error: error!)) } let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) let result = jsonSerializer.serializeResponse(request, response, data, nil) guard case let .success(jsonObject) = result else { return .failure(BackendError.jsonSerialization(error: result.error!)) } guard let response = response else { let reason = "Response collection could not be serialized due to nil response." return .failure(BackendError.objectSerialization(reason: reason)) } return .success(T.collection(from: response, withRepresentation: jsonObject)) } return response(responseSerializer: responseSerializer, completionHandler: completionHandler) } } ``` ```swift struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible { let username: String let name: String var description: String { return "User: { username: \(username), name: \(name) }" } init?(response: HTTPURLResponse, representation: Any) { guard let username = response.url?.lastPathComponent, let representation = representation as? [String: Any], let name = representation["name"] as? String else { return nil } self.username = username self.name = name } } ``` ```swift Alamofire.request("https://example.com/users").responseCollection { (response: DataResponse<[User]>) in debugPrint(response) if let users = response.result.value { users.forEach { print("- \($0)") } } } ``` ### Security Using a secure HTTPS connection when communicating with servers and web services is an important step in securing sensitive data. By default, Alamofire will evaluate the certificate chain provided by the server using Apple's built in validation provided by the Security framework. While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities. In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by the `ServerTrustPolicy`. #### ServerTrustPolicy The `ServerTrustPolicy` enumeration evaluates the server trust generally provided by an `URLAuthenticationChallenge` when connecting to a server over a secure HTTPS connection. ```swift let serverTrustPolicy = ServerTrustPolicy.pinCertificates( certificates: ServerTrustPolicy.certificates(), validateCertificateChain: true, validateHost: true ) ``` There are many different cases of server trust evaluation giving you complete control over the validation process: * `performDefaultEvaluation`: Uses the default server trust evaluation while allowing you to control whether to validate the host provided by the challenge. * `pinCertificates`: Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned certificates match one of the server certificates. * `pinPublicKeys`: Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned public keys match one of the server certificate public keys. * `disableEvaluation`: Disables all evaluation which in turn will always consider any server trust as valid. * `customEvaluation`: Uses the associated closure to evaluate the validity of the server trust thus giving you complete control over the validation process. Use with caution. #### Server Trust Policy Manager The `ServerTrustPolicyManager` is responsible for storing an internal mapping of server trust policies to a particular host. This allows Alamofire to evaluate each host against a different server trust policy. ```swift let serverTrustPolicies: [String: ServerTrustPolicy] = [ "test.example.com": .pinCertificates( certificates: ServerTrustPolicy.certificates(), validateCertificateChain: true, validateHost: true ), "insecure.expired-apis.com": .disableEvaluation ] let sessionManager = SessionManager( serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies) ) ``` > Make sure to keep a reference to the new `SessionManager` instance, otherwise your requests will all get cancelled when your `sessionManager` is deallocated. These server trust policies will result in the following behavior: - `test.example.com` will always use certificate pinning with certificate chain and host validation enabled thus requiring the following criteria to be met to allow the TLS handshake to succeed: - Certificate chain MUST be valid. - Certificate chain MUST include one of the pinned certificates. - Challenge host MUST match the host in the certificate chain's leaf certificate. - `insecure.expired-apis.com` will never evaluate the certificate chain and will always allow the TLS handshake to succeed. - All other hosts will use the default evaluation provided by Apple. ##### Subclassing Server Trust Policy Manager If you find yourself needing more flexible server trust policy matching behavior (i.e. wildcarded domains), then subclass the `ServerTrustPolicyManager` and override the `serverTrustPolicyForHost` method with your own custom implementation. ```swift class CustomServerTrustPolicyManager: ServerTrustPolicyManager { override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? { var policy: ServerTrustPolicy? // Implement your custom domain matching behavior... return policy } } ``` #### Validating the Host The `.performDefaultEvaluation`, `.pinCertificates` and `.pinPublicKeys` server trust policies all take a `validateHost` parameter. Setting the value to `true` will cause the server trust evaluation to verify that hostname in the certificate matches the hostname of the challenge. If they do not match, evaluation will fail. A `validateHost` value of `false` will still evaluate the full certificate chain, but will not validate the hostname of the leaf certificate. > It is recommended that `validateHost` always be set to `true` in production environments. #### Validating the Certificate Chain Pinning certificates and public keys both have the option of validating the certificate chain using the `validateCertificateChain` parameter. By setting this value to `true`, the full certificate chain will be evaluated in addition to performing a byte equality check against the pinned certificates or public keys. A value of `false` will skip the certificate chain validation, but will still perform the byte equality check. There are several cases where it may make sense to disable certificate chain validation. The most common use cases for disabling validation are self-signed and expired certificates. The evaluation would always fail in both of these cases, but the byte equality check will still ensure you are receiving the certificate you expect from the server. > It is recommended that `validateCertificateChain` always be set to `true` in production environments. #### App Transport Security With the addition of App Transport Security (ATS) in iOS 9, it is possible that using a custom `ServerTrustPolicyManager` with several `ServerTrustPolicy` objects will have no effect. If you continuously see `CFNetwork SSLHandshake failed (-9806)` errors, you have probably run into this problem. Apple's ATS system overrides the entire challenge system unless you configure the ATS settings in your app's plist to disable enough of it to allow your app to evaluate the server trust. If you run into this problem (high probability with self-signed certificates), you can work around this issue by adding the following to your `Info.plist`. ```xml NSAppTransportSecurity NSExceptionDomains example.com NSExceptionAllowsInsecureHTTPLoads NSExceptionRequiresForwardSecrecy NSIncludesSubdomains NSTemporaryExceptionMinimumTLSVersion TLSv1.2 ``` Whether you need to set the `NSExceptionRequiresForwardSecrecy` to `NO` depends on whether your TLS connection is using an allowed cipher suite. In certain cases, it will need to be set to `NO`. The `NSExceptionAllowsInsecureHTTPLoads` MUST be set to `YES` in order to allow the `SessionDelegate` to receive challenge callbacks. Once the challenge callbacks are being called, the `ServerTrustPolicyManager` will take over the server trust evaluation. You may also need to specify the `NSTemporaryExceptionMinimumTLSVersion` if you're trying to connect to a host that only supports TLS versions less than `1.2`. > It is recommended to always use valid certificates in production environments. ### Network Reachability The `NetworkReachabilityManager` listens for reachability changes of hosts and addresses for both WWAN and WiFi network interfaces. ```swift let manager = NetworkReachabilityManager(host: "www.apple.com") manager?.listener = { status in print("Network Status Changed: \(status)") } manager?.startListening() ``` > Make sure to remember to retain the `manager` in the above example, or no status changes will be reported. > Also, do not include the scheme in the `host` string or reachability won't function correctly. There are some important things to remember when using network reachability to determine what to do next. - **Do NOT** use Reachability to determine if a network request should be sent. - You should **ALWAYS** send it. - When Reachability is restored, use the event to retry failed network requests. - Even though the network requests may still fail, this is a good moment to retry them. - The network reachability status can be useful for determining why a network request may have failed. - If a network request fails, it is more useful to tell the user that the network request failed due to being offline rather than a more technical error, such as "request timed out." > It is recommended to check out [WWDC 2012 Session 706, "Networking Best Practices"](https://developer.apple.com/videos/play/wwdc2012-706/) for more info. --- ## Open Radars The following radars have some effect on the current implementation of Alamofire. - [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case - `rdar://26870455` - Background URL Session Configurations do not work in the simulator - `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` ## Resolved Radars The following radars have been resolved over time after being filed against the Alamofire project. - [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage (Resolved on 9/1/17 in Xcode 9 beta 6). ## FAQ ### What's the origin of the name Alamofire? Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas. ### What logic belongs in a Router vs. a Request Adapter? Simple, static data such as paths, parameters and common headers belong in the `Router`. Dynamic data such as an `Authorization` header whose value can changed based on an authentication system belongs in a `RequestAdapter`. The reason the dynamic data MUST be placed into the `RequestAdapter` is to support retry operations. When a `Request` is retried, the original request is not rebuilt meaning the `Router` will not be called again. The `RequestAdapter` is called again allowing the dynamic data to be updated on the original request before retrying the `Request`. --- ## Credits Alamofire is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). You can follow them on Twitter at [@AlamofireSF](https://twitter.com/AlamofireSF) for project updates and releases. ### Security Disclosure If you believe you have identified a security vulnerability with Alamofire, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker. ## Donations The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially register as a federal non-profit organization. Registering will allow us members to gain some legal protections and also allow us to put donations to use, tax free. Donating to the ASF will enable us to: - Pay our legal fees to register as a federal non-profit organization - Pay our yearly legal fees to keep the non-profit in good status - Pay for our mail servers to help us stay on top of all questions and security issues - Potentially fund test servers to make it easier for us to test the edge cases - Potentially fund developers to work on one of our projects full-time The community adoption of the ASF libraries has been amazing. We are greatly humbled by your enthusiasm around the projects, and want to continue to do everything we can to move the needle forward. With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. If you use any of our libraries for work, see if your employers would be interested in donating. Our initial goal is to raise $1000 to get all our legal ducks in a row and kickstart this campaign. Any amount you can donate today to help us reach our goal would be greatly appreciated. Click here to lend your support to: Alamofire Software Foundation and make a donation at pledgie.com ! ## License Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details. ================================================ FILE: Pods/Alamofire/Source/AFError.swift ================================================ // // AFError.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with /// their own associated reasons. /// /// - invalidURL: Returned when a `URLConvertible` type fails to create a valid `URL`. /// - parameterEncodingFailed: Returned when a parameter encoding object throws an error during the encoding process. /// - multipartEncodingFailed: Returned when some step in the multipart encoding process fails. /// - responseValidationFailed: Returned when a `validate()` call fails. /// - responseSerializationFailed: Returned when a response serializer encounters an error in the serialization process. public enum AFError: Error { /// The underlying reason the parameter encoding error occurred. /// /// - missingURL: The URL request did not have a URL to encode. /// - jsonEncodingFailed: JSON serialization failed with an underlying system error during the /// encoding process. /// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during /// encoding process. public enum ParameterEncodingFailureReason { case missingURL case jsonEncodingFailed(error: Error) case propertyListEncodingFailed(error: Error) } /// The underlying reason the multipart encoding error occurred. /// /// - bodyPartURLInvalid: The `fileURL` provided for reading an encodable body part isn't a /// file URL. /// - bodyPartFilenameInvalid: The filename of the `fileURL` provided has either an empty /// `lastPathComponent` or `pathExtension. /// - bodyPartFileNotReachable: The file at the `fileURL` provided was not reachable. /// - bodyPartFileNotReachableWithError: Attempting to check the reachability of the `fileURL` provided threw /// an error. /// - bodyPartFileIsDirectory: The file at the `fileURL` provided is actually a directory. /// - bodyPartFileSizeNotAvailable: The size of the file at the `fileURL` provided was not returned by /// the system. /// - bodyPartFileSizeQueryFailedWithError: The attempt to find the size of the file at the `fileURL` provided /// threw an error. /// - bodyPartInputStreamCreationFailed: An `InputStream` could not be created for the provided `fileURL`. /// - outputStreamCreationFailed: An `OutputStream` could not be created when attempting to write the /// encoded data to disk. /// - outputStreamFileAlreadyExists: The encoded body data could not be writtent disk because a file /// already exists at the provided `fileURL`. /// - outputStreamURLInvalid: The `fileURL` provided for writing the encoded body data to disk is /// not a file URL. /// - outputStreamWriteFailed: The attempt to write the encoded body data to disk failed with an /// underlying error. /// - inputStreamReadFailed: The attempt to read an encoded body part `InputStream` failed with /// underlying system error. public enum MultipartEncodingFailureReason { case bodyPartURLInvalid(url: URL) case bodyPartFilenameInvalid(in: URL) case bodyPartFileNotReachable(at: URL) case bodyPartFileNotReachableWithError(atURL: URL, error: Error) case bodyPartFileIsDirectory(at: URL) case bodyPartFileSizeNotAvailable(at: URL) case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) case bodyPartInputStreamCreationFailed(for: URL) case outputStreamCreationFailed(for: URL) case outputStreamFileAlreadyExists(at: URL) case outputStreamURLInvalid(url: URL) case outputStreamWriteFailed(error: Error) case inputStreamReadFailed(error: Error) } /// The underlying reason the response validation error occurred. /// /// - dataFileNil: The data file containing the server response did not exist. /// - dataFileReadFailed: The data file containing the server response could not be read. /// - missingContentType: The response did not contain a `Content-Type` and the `acceptableContentTypes` /// provided did not contain wildcard type. /// - unacceptableContentType: The response `Content-Type` did not match any type in the provided /// `acceptableContentTypes`. /// - unacceptableStatusCode: The response status code was not acceptable. public enum ResponseValidationFailureReason { case dataFileNil case dataFileReadFailed(at: URL) case missingContentType(acceptableContentTypes: [String]) case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) case unacceptableStatusCode(code: Int) } /// The underlying reason the response serialization error occurred. /// /// - inputDataNil: The server response contained no data. /// - inputDataNilOrZeroLength: The server response contained no data or the data was zero length. /// - inputFileNil: The file containing the server response did not exist. /// - inputFileReadFailed: The file containing the server response could not be read. /// - stringSerializationFailed: String serialization failed using the provided `String.Encoding`. /// - jsonSerializationFailed: JSON serialization failed with an underlying system error. /// - propertyListSerializationFailed: Property list serialization failed with an underlying system error. public enum ResponseSerializationFailureReason { case inputDataNil case inputDataNilOrZeroLength case inputFileNil case inputFileReadFailed(at: URL) case stringSerializationFailed(encoding: String.Encoding) case jsonSerializationFailed(error: Error) case propertyListSerializationFailed(error: Error) } case invalidURL(url: URLConvertible) case parameterEncodingFailed(reason: ParameterEncodingFailureReason) case multipartEncodingFailed(reason: MultipartEncodingFailureReason) case responseValidationFailed(reason: ResponseValidationFailureReason) case responseSerializationFailed(reason: ResponseSerializationFailureReason) } // MARK: - Adapt Error struct AdaptError: Error { let error: Error } extension Error { var underlyingAdaptError: Error? { return (self as? AdaptError)?.error } } // MARK: - Error Booleans extension AFError { /// Returns whether the AFError is an invalid URL error. public var isInvalidURLError: Bool { if case .invalidURL = self { return true } return false } /// Returns whether the AFError is a parameter encoding error. When `true`, the `underlyingError` property will /// contain the associated value. public var isParameterEncodingError: Bool { if case .parameterEncodingFailed = self { return true } return false } /// Returns whether the AFError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties /// will contain the associated values. public var isMultipartEncodingError: Bool { if case .multipartEncodingFailed = self { return true } return false } /// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`, /// `responseContentType`, and `responseCode` properties will contain the associated values. public var isResponseValidationError: Bool { if case .responseValidationFailed = self { return true } return false } /// Returns whether the `AFError` is a response serialization error. When `true`, the `failedStringEncoding` and /// `underlyingError` properties will contain the associated values. public var isResponseSerializationError: Bool { if case .responseSerializationFailed = self { return true } return false } } // MARK: - Convenience Properties extension AFError { /// The `URLConvertible` associated with the error. public var urlConvertible: URLConvertible? { switch self { case .invalidURL(let url): return url default: return nil } } /// The `URL` associated with the error. public var url: URL? { switch self { case .multipartEncodingFailed(let reason): return reason.url default: return nil } } /// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`, /// `.multipartEncodingFailed` or `.responseSerializationFailed` error. public var underlyingError: Error? { switch self { case .parameterEncodingFailed(let reason): return reason.underlyingError case .multipartEncodingFailed(let reason): return reason.underlyingError case .responseSerializationFailed(let reason): return reason.underlyingError default: return nil } } /// The acceptable `Content-Type`s of a `.responseValidationFailed` error. public var acceptableContentTypes: [String]? { switch self { case .responseValidationFailed(let reason): return reason.acceptableContentTypes default: return nil } } /// The response `Content-Type` of a `.responseValidationFailed` error. public var responseContentType: String? { switch self { case .responseValidationFailed(let reason): return reason.responseContentType default: return nil } } /// The response code of a `.responseValidationFailed` error. public var responseCode: Int? { switch self { case .responseValidationFailed(let reason): return reason.responseCode default: return nil } } /// The `String.Encoding` associated with a failed `.stringResponse()` call. public var failedStringEncoding: String.Encoding? { switch self { case .responseSerializationFailed(let reason): return reason.failedStringEncoding default: return nil } } } extension AFError.ParameterEncodingFailureReason { var underlyingError: Error? { switch self { case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error): return error default: return nil } } } extension AFError.MultipartEncodingFailureReason { var url: URL? { switch self { case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url), .bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url), .bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url), .outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url), .bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _): return url default: return nil } } var underlyingError: Error? { switch self { case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error), .outputStreamWriteFailed(let error), .inputStreamReadFailed(let error): return error default: return nil } } } extension AFError.ResponseValidationFailureReason { var acceptableContentTypes: [String]? { switch self { case .missingContentType(let types), .unacceptableContentType(let types, _): return types default: return nil } } var responseContentType: String? { switch self { case .unacceptableContentType(_, let responseType): return responseType default: return nil } } var responseCode: Int? { switch self { case .unacceptableStatusCode(let code): return code default: return nil } } } extension AFError.ResponseSerializationFailureReason { var failedStringEncoding: String.Encoding? { switch self { case .stringSerializationFailed(let encoding): return encoding default: return nil } } var underlyingError: Error? { switch self { case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error): return error default: return nil } } } // MARK: - Error Descriptions extension AFError: LocalizedError { public var errorDescription: String? { switch self { case .invalidURL(let url): return "URL is not valid: \(url)" case .parameterEncodingFailed(let reason): return reason.localizedDescription case .multipartEncodingFailed(let reason): return reason.localizedDescription case .responseValidationFailed(let reason): return reason.localizedDescription case .responseSerializationFailed(let reason): return reason.localizedDescription } } } extension AFError.ParameterEncodingFailureReason { var localizedDescription: String { switch self { case .missingURL: return "URL request to encode was missing a URL" case .jsonEncodingFailed(let error): return "JSON could not be encoded because of error:\n\(error.localizedDescription)" case .propertyListEncodingFailed(let error): return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)" } } } extension AFError.MultipartEncodingFailureReason { var localizedDescription: String { switch self { case .bodyPartURLInvalid(let url): return "The URL provided is not a file URL: \(url)" case .bodyPartFilenameInvalid(let url): return "The URL provided does not have a valid filename: \(url)" case .bodyPartFileNotReachable(let url): return "The URL provided is not reachable: \(url)" case .bodyPartFileNotReachableWithError(let url, let error): return ( "The system returned an error while checking the provided URL for " + "reachability.\nURL: \(url)\nError: \(error)" ) case .bodyPartFileIsDirectory(let url): return "The URL provided is a directory: \(url)" case .bodyPartFileSizeNotAvailable(let url): return "Could not fetch the file size from the provided URL: \(url)" case .bodyPartFileSizeQueryFailedWithError(let url, let error): return ( "The system returned an error while attempting to fetch the file size from the " + "provided URL.\nURL: \(url)\nError: \(error)" ) case .bodyPartInputStreamCreationFailed(let url): return "Failed to create an InputStream for the provided URL: \(url)" case .outputStreamCreationFailed(let url): return "Failed to create an OutputStream for URL: \(url)" case .outputStreamFileAlreadyExists(let url): return "A file already exists at the provided URL: \(url)" case .outputStreamURLInvalid(let url): return "The provided OutputStream URL is invalid: \(url)" case .outputStreamWriteFailed(let error): return "OutputStream write failed with error: \(error)" case .inputStreamReadFailed(let error): return "InputStream read failed with error: \(error)" } } } extension AFError.ResponseSerializationFailureReason { var localizedDescription: String { switch self { case .inputDataNil: return "Response could not be serialized, input data was nil." case .inputDataNilOrZeroLength: return "Response could not be serialized, input data was nil or zero length." case .inputFileNil: return "Response could not be serialized, input file was nil." case .inputFileReadFailed(let url): return "Response could not be serialized, input file could not be read: \(url)." case .stringSerializationFailed(let encoding): return "String could not be serialized with encoding: \(encoding)." case .jsonSerializationFailed(let error): return "JSON could not be serialized because of error:\n\(error.localizedDescription)" case .propertyListSerializationFailed(let error): return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)" } } } extension AFError.ResponseValidationFailureReason { var localizedDescription: String { switch self { case .dataFileNil: return "Response could not be validated, data file was nil." case .dataFileReadFailed(let url): return "Response could not be validated, data file could not be read: \(url)." case .missingContentType(let types): return ( "Response Content-Type was missing and acceptable content types " + "(\(types.joined(separator: ","))) do not match \"*/*\"." ) case .unacceptableContentType(let acceptableTypes, let responseType): return ( "Response Content-Type \"\(responseType)\" does not match any acceptable types: " + "\(acceptableTypes.joined(separator: ","))." ) case .unacceptableStatusCode(let code): return "Response status code was unacceptable: \(code)." } } } ================================================ FILE: Pods/Alamofire/Source/Alamofire.swift ================================================ // // Alamofire.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct /// URL requests. public protocol URLConvertible { /// Returns a URL that conforms to RFC 2396 or throws an `Error`. /// /// - throws: An `Error` if the type cannot be converted to a `URL`. /// /// - returns: A URL or throws an `Error`. func asURL() throws -> URL } extension String: URLConvertible { /// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `AFError`. /// /// - throws: An `AFError.invalidURL` if `self` is not a valid URL string. /// /// - returns: A URL or throws an `AFError`. public func asURL() throws -> URL { guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) } return url } } extension URL: URLConvertible { /// Returns self. public func asURL() throws -> URL { return self } } extension URLComponents: URLConvertible { /// Returns a URL if `url` is not nil, otherwise throws an `Error`. /// /// - throws: An `AFError.invalidURL` if `url` is `nil`. /// /// - returns: A URL or throws an `AFError`. public func asURL() throws -> URL { guard let url = url else { throw AFError.invalidURL(url: self) } return url } } // MARK: - /// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. public protocol URLRequestConvertible { /// Returns a URL request or throws if an `Error` was encountered. /// /// - throws: An `Error` if the underlying `URLRequest` is `nil`. /// /// - returns: A URL request. func asURLRequest() throws -> URLRequest } extension URLRequestConvertible { /// The URL request. public var urlRequest: URLRequest? { return try? asURLRequest() } } extension URLRequest: URLRequestConvertible { /// Returns a URL request or throws if an `Error` was encountered. public func asURLRequest() throws -> URLRequest { return self } } // MARK: - extension URLRequest { /// Creates an instance with the specified `method`, `urlString` and `headers`. /// /// - parameter url: The URL. /// - parameter method: The HTTP method. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The new `URLRequest` instance. public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws { let url = try url.asURL() self.init(url: url) httpMethod = method.rawValue if let headers = headers { for (headerField, headerValue) in headers { setValue(headerValue, forHTTPHeaderField: headerField) } } } func adapt(using adapter: RequestAdapter?) throws -> URLRequest { guard let adapter = adapter else { return self } return try adapter.adapt(self) } } // MARK: - Data Request /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, /// `method`, `parameters`, `encoding` and `headers`. /// /// - parameter url: The URL. /// - parameter method: The HTTP method. `.get` by default. /// - parameter parameters: The parameters. `nil` by default. /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `DataRequest`. @discardableResult public func request( _ url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) -> DataRequest { return SessionManager.default.request( url, method: method, parameters: parameters, encoding: encoding, headers: headers ) } /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the /// specified `urlRequest`. /// /// - parameter urlRequest: The URL request /// /// - returns: The created `DataRequest`. @discardableResult public func request(_ urlRequest: URLRequestConvertible) -> DataRequest { return SessionManager.default.request(urlRequest) } // MARK: - Download Request // MARK: URL Request /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of the specified `url`, /// `method`, `parameters`, `encoding`, `headers` and save them to the `destination`. /// /// If `destination` is not specified, the contents will remain in the temporary location determined by the /// underlying URL session. /// /// - parameter url: The URL. /// - parameter method: The HTTP method. `.get` by default. /// - parameter parameters: The parameters. `nil` by default. /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. /// /// - returns: The created `DownloadRequest`. @discardableResult public func download( _ url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil, to destination: DownloadRequest.DownloadFileDestination? = nil) -> DownloadRequest { return SessionManager.default.download( url, method: method, parameters: parameters, encoding: encoding, headers: headers, to: destination ) } /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the /// specified `urlRequest` and save them to the `destination`. /// /// If `destination` is not specified, the contents will remain in the temporary location determined by the /// underlying URL session. /// /// - parameter urlRequest: The URL request. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. /// /// - returns: The created `DownloadRequest`. @discardableResult public func download( _ urlRequest: URLRequestConvertible, to destination: DownloadRequest.DownloadFileDestination? = nil) -> DownloadRequest { return SessionManager.default.download(urlRequest, to: destination) } // MARK: Resume Data /// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a /// previous request cancellation to retrieve the contents of the original request and save them to the `destination`. /// /// If `destination` is not specified, the contents will remain in the temporary location determined by the /// underlying URL session. /// /// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken /// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the /// data is written incorrectly and will always fail to resume the download. For more information about the bug and /// possible workarounds, please refer to the following Stack Overflow post: /// /// - http://stackoverflow.com/a/39347461/1342462 /// /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask` /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional /// information. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. /// /// - returns: The created `DownloadRequest`. @discardableResult public func download( resumingWith resumeData: Data, to destination: DownloadRequest.DownloadFileDestination? = nil) -> DownloadRequest { return SessionManager.default.download(resumingWith: resumeData, to: destination) } // MARK: - Upload Request // MARK: File /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` /// for uploading the `file`. /// /// - parameter file: The file to upload. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `UploadRequest`. @discardableResult public func upload( _ fileURL: URL, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil) -> UploadRequest { return SessionManager.default.upload(fileURL, to: url, method: method, headers: headers) } /// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for /// uploading the `file`. /// /// - parameter file: The file to upload. /// - parameter urlRequest: The URL request. /// /// - returns: The created `UploadRequest`. @discardableResult public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest { return SessionManager.default.upload(fileURL, with: urlRequest) } // MARK: Data /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` /// for uploading the `data`. /// /// - parameter data: The data to upload. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `UploadRequest`. @discardableResult public func upload( _ data: Data, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil) -> UploadRequest { return SessionManager.default.upload(data, to: url, method: method, headers: headers) } /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for /// uploading the `data`. /// /// - parameter data: The data to upload. /// - parameter urlRequest: The URL request. /// /// - returns: The created `UploadRequest`. @discardableResult public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest { return SessionManager.default.upload(data, with: urlRequest) } // MARK: InputStream /// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers` /// for uploading the `stream`. /// /// - parameter stream: The stream to upload. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `UploadRequest`. @discardableResult public func upload( _ stream: InputStream, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil) -> UploadRequest { return SessionManager.default.upload(stream, to: url, method: method, headers: headers) } /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for /// uploading the `stream`. /// /// - parameter urlRequest: The URL request. /// - parameter stream: The stream to upload. /// /// - returns: The created `UploadRequest`. @discardableResult public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest { return SessionManager.default.upload(stream, with: urlRequest) } // MARK: MultipartFormData /// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls /// `encodingCompletion` with new `UploadRequest` using the `url`, `method` and `headers`. /// /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be /// used for larger payloads such as video content. /// /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding /// technique was used. /// /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. /// `multipartFormDataEncodingMemoryThreshold` by default. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. public func upload( multipartFormData: @escaping (MultipartFormData) -> Void, usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil, encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) { return SessionManager.default.upload( multipartFormData: multipartFormData, usingThreshold: encodingMemoryThreshold, to: url, method: method, headers: headers, encodingCompletion: encodingCompletion ) } /// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and /// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`. /// /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be /// used for larger payloads such as video content. /// /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding /// technique was used. /// /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. /// `multipartFormDataEncodingMemoryThreshold` by default. /// - parameter urlRequest: The URL request. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. public func upload( multipartFormData: @escaping (MultipartFormData) -> Void, usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, with urlRequest: URLRequestConvertible, encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) { return SessionManager.default.upload( multipartFormData: multipartFormData, usingThreshold: encodingMemoryThreshold, with: urlRequest, encodingCompletion: encodingCompletion ) } #if !os(watchOS) // MARK: - Stream Request // MARK: Hostname and Port /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname` /// and `port`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter hostName: The hostname of the server to connect to. /// - parameter port: The port of the server to connect to. /// /// - returns: The created `StreamRequest`. @discardableResult @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) public func stream(withHostName hostName: String, port: Int) -> StreamRequest { return SessionManager.default.stream(withHostName: hostName, port: port) } // MARK: NetService /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter netService: The net service used to identify the endpoint. /// /// - returns: The created `StreamRequest`. @discardableResult @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) public func stream(with netService: NetService) -> StreamRequest { return SessionManager.default.stream(with: netService) } #endif ================================================ FILE: Pods/Alamofire/Source/DispatchQueue+Alamofire.swift ================================================ // // DispatchQueue+Alamofire.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Dispatch import Foundation extension DispatchQueue { static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { asyncAfter(deadline: .now() + delay, execute: closure) } } ================================================ FILE: Pods/Alamofire/Source/MultipartFormData.swift ================================================ // // MultipartFormData.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation #if os(iOS) || os(watchOS) || os(tvOS) import MobileCoreServices #elseif os(macOS) import CoreServices #endif /// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode /// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead /// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the /// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for /// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset. /// /// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well /// and the w3 form documentation. /// /// - https://www.ietf.org/rfc/rfc2388.txt /// - https://www.ietf.org/rfc/rfc2045.txt /// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13 open class MultipartFormData { // MARK: - Helper Types struct EncodingCharacters { static let crlf = "\r\n" } struct BoundaryGenerator { enum BoundaryType { case initial, encapsulated, final } static func randomBoundary() -> String { return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random()) } static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data { let boundaryText: String switch boundaryType { case .initial: boundaryText = "--\(boundary)\(EncodingCharacters.crlf)" case .encapsulated: boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)" case .final: boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)" } return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)! } } class BodyPart { let headers: HTTPHeaders let bodyStream: InputStream let bodyContentLength: UInt64 var hasInitialBoundary = false var hasFinalBoundary = false init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) { self.headers = headers self.bodyStream = bodyStream self.bodyContentLength = bodyContentLength } } // MARK: - Properties /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`. open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)" /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries. public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } } /// The boundary used to separate the body parts in the encoded form data. public let boundary: String private var bodyParts: [BodyPart] private var bodyPartError: AFError? private let streamBufferSize: Int // MARK: - Lifecycle /// Creates a multipart form data object. /// /// - returns: The multipart form data object. public init() { self.boundary = BoundaryGenerator.randomBoundary() self.bodyParts = [] /// /// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more /// information, please refer to the following article: /// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html /// self.streamBufferSize = 1024 } // MARK: - Body Parts /// Creates a body part from the data and appends it to the multipart form data object. /// /// The body part data will be encoded using the following format: /// /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header) /// - Encoded data /// - Multipart form boundary /// /// - parameter data: The data to encode into the multipart form data. /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. public func append(_ data: Data, withName name: String) { let headers = contentHeaders(withName: name) let stream = InputStream(data: data) let length = UInt64(data.count) append(stream, withLength: length, headers: headers) } /// Creates a body part from the data and appends it to the multipart form data object. /// /// The body part data will be encoded using the following format: /// /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header) /// - `Content-Type: #{generated mimeType}` (HTTP Header) /// - Encoded data /// - Multipart form boundary /// /// - parameter data: The data to encode into the multipart form data. /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. /// - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header. public func append(_ data: Data, withName name: String, mimeType: String) { let headers = contentHeaders(withName: name, mimeType: mimeType) let stream = InputStream(data: data) let length = UInt64(data.count) append(stream, withLength: length, headers: headers) } /// Creates a body part from the data and appends it to the multipart form data object. /// /// The body part data will be encoded using the following format: /// /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) /// - `Content-Type: #{mimeType}` (HTTP Header) /// - Encoded file data /// - Multipart form boundary /// /// - parameter data: The data to encode into the multipart form data. /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. /// - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header. /// - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header. public func append(_ data: Data, withName name: String, fileName: String, mimeType: String) { let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) let stream = InputStream(data: data) let length = UInt64(data.count) append(stream, withLength: length, headers: headers) } /// Creates a body part from the file and appends it to the multipart form data object. /// /// The body part data will be encoded using the following format: /// /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header) /// - `Content-Type: #{generated mimeType}` (HTTP Header) /// - Encoded file data /// - Multipart form boundary /// /// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the /// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the /// system associated MIME type. /// /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. public func append(_ fileURL: URL, withName name: String) { let fileName = fileURL.lastPathComponent let pathExtension = fileURL.pathExtension if !fileName.isEmpty && !pathExtension.isEmpty { let mime = mimeType(forPathExtension: pathExtension) append(fileURL, withName: name, fileName: fileName, mimeType: mime) } else { setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL)) } } /// Creates a body part from the file and appends it to the multipart form data object. /// /// The body part data will be encoded using the following format: /// /// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header) /// - Content-Type: #{mimeType} (HTTP Header) /// - Encoded file data /// - Multipart form boundary /// /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. /// - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header. /// - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header. public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) { let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) //============================================================ // Check 1 - is file URL? //============================================================ guard fileURL.isFileURL else { setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL)) return } //============================================================ // Check 2 - is file URL reachable? //============================================================ do { let isReachable = try fileURL.checkPromisedItemIsReachable() guard isReachable else { setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL)) return } } catch { setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error)) return } //============================================================ // Check 3 - is file URL a directory? //============================================================ var isDirectory: ObjCBool = false let path = fileURL.path guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else { setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL)) return } //============================================================ // Check 4 - can the file size be extracted? //============================================================ let bodyContentLength: UInt64 do { guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else { setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL)) return } bodyContentLength = fileSize.uint64Value } catch { setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error)) return } //============================================================ // Check 5 - can a stream be created from file URL? //============================================================ guard let stream = InputStream(url: fileURL) else { setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL)) return } append(stream, withLength: bodyContentLength, headers: headers) } /// Creates a body part from the stream and appends it to the multipart form data object. /// /// The body part data will be encoded using the following format: /// /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) /// - `Content-Type: #{mimeType}` (HTTP Header) /// - Encoded stream data /// - Multipart form boundary /// /// - parameter stream: The input stream to encode in the multipart form data. /// - parameter length: The content length of the stream. /// - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header. /// - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header. /// - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header. public func append( _ stream: InputStream, withLength length: UInt64, name: String, fileName: String, mimeType: String) { let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType) append(stream, withLength: length, headers: headers) } /// Creates a body part with the headers, stream and length and appends it to the multipart form data object. /// /// The body part data will be encoded using the following format: /// /// - HTTP headers /// - Encoded stream data /// - Multipart form boundary /// /// - parameter stream: The input stream to encode in the multipart form data. /// - parameter length: The content length of the stream. /// - parameter headers: The HTTP headers for the body part. public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) { let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length) bodyParts.append(bodyPart) } // MARK: - Data Encoding /// Encodes all the appended body parts into a single `Data` value. /// /// It is important to note that this method will load all the appended body parts into memory all at the same /// time. This method should only be used when the encoded data will have a small memory footprint. For large data /// cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method. /// /// - throws: An `AFError` if encoding encounters an error. /// /// - returns: The encoded `Data` if encoding is successful. public func encode() throws -> Data { if let bodyPartError = bodyPartError { throw bodyPartError } var encoded = Data() bodyParts.first?.hasInitialBoundary = true bodyParts.last?.hasFinalBoundary = true for bodyPart in bodyParts { let encodedData = try encode(bodyPart) encoded.append(encodedData) } return encoded } /// Writes the appended body parts into the given file URL. /// /// This process is facilitated by reading and writing with input and output streams, respectively. Thus, /// this approach is very memory efficient and should be used for large body part data. /// /// - parameter fileURL: The file URL to write the multipart form data into. /// /// - throws: An `AFError` if encoding encounters an error. public func writeEncodedData(to fileURL: URL) throws { if let bodyPartError = bodyPartError { throw bodyPartError } if FileManager.default.fileExists(atPath: fileURL.path) { throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL)) } else if !fileURL.isFileURL { throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL)) } guard let outputStream = OutputStream(url: fileURL, append: false) else { throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL)) } outputStream.open() defer { outputStream.close() } self.bodyParts.first?.hasInitialBoundary = true self.bodyParts.last?.hasFinalBoundary = true for bodyPart in self.bodyParts { try write(bodyPart, to: outputStream) } } // MARK: - Private - Body Part Encoding private func encode(_ bodyPart: BodyPart) throws -> Data { var encoded = Data() let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() encoded.append(initialData) let headerData = encodeHeaders(for: bodyPart) encoded.append(headerData) let bodyStreamData = try encodeBodyStream(for: bodyPart) encoded.append(bodyStreamData) if bodyPart.hasFinalBoundary { encoded.append(finalBoundaryData()) } return encoded } private func encodeHeaders(for bodyPart: BodyPart) -> Data { var headerText = "" for (key, value) in bodyPart.headers { headerText += "\(key): \(value)\(EncodingCharacters.crlf)" } headerText += EncodingCharacters.crlf return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)! } private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data { let inputStream = bodyPart.bodyStream inputStream.open() defer { inputStream.close() } var encoded = Data() while inputStream.hasBytesAvailable { var buffer = [UInt8](repeating: 0, count: streamBufferSize) let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) if let error = inputStream.streamError { throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error)) } if bytesRead > 0 { encoded.append(buffer, count: bytesRead) } else { break } } return encoded } // MARK: - Private - Writing Body Part to Output Stream private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws { try writeInitialBoundaryData(for: bodyPart, to: outputStream) try writeHeaderData(for: bodyPart, to: outputStream) try writeBodyStream(for: bodyPart, to: outputStream) try writeFinalBoundaryData(for: bodyPart, to: outputStream) } private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() return try write(initialData, to: outputStream) } private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws { let headerData = encodeHeaders(for: bodyPart) return try write(headerData, to: outputStream) } private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws { let inputStream = bodyPart.bodyStream inputStream.open() defer { inputStream.close() } while inputStream.hasBytesAvailable { var buffer = [UInt8](repeating: 0, count: streamBufferSize) let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) if let streamError = inputStream.streamError { throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError)) } if bytesRead > 0 { if buffer.count != bytesRead { buffer = Array(buffer[0.. 0, outputStream.hasSpaceAvailable { let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite) if let error = outputStream.streamError { throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error)) } bytesToWrite -= bytesWritten if bytesToWrite > 0 { buffer = Array(buffer[bytesWritten.. String { if let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() { return contentType as String } return "application/octet-stream" } // MARK: - Private - Content Headers private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] { var disposition = "form-data; name=\"\(name)\"" if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" } var headers = ["Content-Disposition": disposition] if let mimeType = mimeType { headers["Content-Type"] = mimeType } return headers } // MARK: - Private - Boundary Encoding private func initialBoundaryData() -> Data { return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary) } private func encapsulatedBoundaryData() -> Data { return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary) } private func finalBoundaryData() -> Data { return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary) } // MARK: - Private - Errors private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) { guard bodyPartError == nil else { return } bodyPartError = AFError.multipartEncodingFailed(reason: reason) } } ================================================ FILE: Pods/Alamofire/Source/NetworkReachabilityManager.swift ================================================ // // NetworkReachabilityManager.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // #if !os(watchOS) import Foundation import SystemConfiguration /// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and /// WiFi network interfaces. /// /// Reachability can be used to determine background information about why a network operation failed, or to retry /// network requests when a connection is established. It should not be used to prevent a user from initiating a network /// request, as it's possible that an initial request may be required to establish reachability. public class NetworkReachabilityManager { /// Defines the various states of network reachability. /// /// - unknown: It is unknown whether the network is reachable. /// - notReachable: The network is not reachable. /// - reachable: The network is reachable. public enum NetworkReachabilityStatus { case unknown case notReachable case reachable(ConnectionType) } /// Defines the various connection types detected by reachability flags. /// /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi. /// - wwan: The connection type is a WWAN connection. public enum ConnectionType { case ethernetOrWiFi case wwan } /// A closure executed when the network reachability status changes. The closure takes a single argument: the /// network reachability status. public typealias Listener = (NetworkReachabilityStatus) -> Void // MARK: - Properties /// Whether the network is currently reachable. public var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } /// Whether the network is currently reachable over the WWAN interface. public var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } /// Whether the network is currently reachable over Ethernet or WiFi interface. public var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } /// The current network reachability status. public var networkReachabilityStatus: NetworkReachabilityStatus { guard let flags = self.flags else { return .unknown } return networkReachabilityStatusForFlags(flags) } /// The dispatch queue to execute the `listener` closure on. public var listenerQueue: DispatchQueue = DispatchQueue.main /// A closure executed when the network reachability status changes. public var listener: Listener? private var flags: SCNetworkReachabilityFlags? { var flags = SCNetworkReachabilityFlags() if SCNetworkReachabilityGetFlags(reachability, &flags) { return flags } return nil } private let reachability: SCNetworkReachability private var previousFlags: SCNetworkReachabilityFlags // MARK: - Initialization /// Creates a `NetworkReachabilityManager` instance with the specified host. /// /// - parameter host: The host used to evaluate network reachability. /// /// - returns: The new `NetworkReachabilityManager` instance. public convenience init?(host: String) { guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } self.init(reachability: reachability) } /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0. /// /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing /// status of the device, both IPv4 and IPv6. /// /// - returns: The new `NetworkReachabilityManager` instance. public convenience init?() { var address = sockaddr_in() address.sin_len = UInt8(MemoryLayout.size) address.sin_family = sa_family_t(AF_INET) guard let reachability = withUnsafePointer(to: &address, { pointer in return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout.size) { return SCNetworkReachabilityCreateWithAddress(nil, $0) } }) else { return nil } self.init(reachability: reachability) } private init(reachability: SCNetworkReachability) { self.reachability = reachability self.previousFlags = SCNetworkReachabilityFlags() } deinit { stopListening() } // MARK: - Listening /// Starts listening for changes in network reachability status. /// /// - returns: `true` if listening was started successfully, `false` otherwise. @discardableResult public func startListening() -> Bool { var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) context.info = Unmanaged.passUnretained(self).toOpaque() let callbackEnabled = SCNetworkReachabilitySetCallback( reachability, { (_, flags, info) in let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() reachability.notifyListener(flags) }, &context ) let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue) listenerQueue.async { self.previousFlags = SCNetworkReachabilityFlags() self.notifyListener(self.flags ?? SCNetworkReachabilityFlags()) } return callbackEnabled && queueEnabled } /// Stops listening for changes in network reachability status. public func stopListening() { SCNetworkReachabilitySetCallback(reachability, nil, nil) SCNetworkReachabilitySetDispatchQueue(reachability, nil) } // MARK: - Internal - Listener Notification func notifyListener(_ flags: SCNetworkReachabilityFlags) { guard previousFlags != flags else { return } previousFlags = flags listener?(networkReachabilityStatusForFlags(flags)) } // MARK: - Internal - Network Reachability Status func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { guard isNetworkReachable(with: flags) else { return .notReachable } var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) #if os(iOS) if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } #endif return networkStatus } func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool { let isReachable = flags.contains(.reachable) let needsConnection = flags.contains(.connectionRequired) let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired) return isReachable && (!needsConnection || canConnectWithoutUserInteraction) } } // MARK: - extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} /// Returns whether the two network reachability status values are equal. /// /// - parameter lhs: The left-hand side value to compare. /// - parameter rhs: The right-hand side value to compare. /// /// - returns: `true` if the two values are equal, `false` otherwise. public func ==( lhs: NetworkReachabilityManager.NetworkReachabilityStatus, rhs: NetworkReachabilityManager.NetworkReachabilityStatus) -> Bool { switch (lhs, rhs) { case (.unknown, .unknown): return true case (.notReachable, .notReachable): return true case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)): return lhsConnectionType == rhsConnectionType default: return false } } #endif ================================================ FILE: Pods/Alamofire/Source/Notifications.swift ================================================ // // Notifications.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation extension Notification.Name { /// Used as a namespace for all `URLSessionTask` related notifications. public struct Task { /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") } } // MARK: - extension Notification { /// Used as a namespace for all `Notification` user info dictionary keys. public struct Key { /// User info dictionary key representing the `URLSessionTask` associated with the notification. public static let Task = "org.alamofire.notification.key.task" } } ================================================ FILE: Pods/Alamofire/Source/ParameterEncoding.swift ================================================ // // ParameterEncoding.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// HTTP method definitions. /// /// See https://tools.ietf.org/html/rfc7231#section-4.3 public enum HTTPMethod: String { case options = "OPTIONS" case get = "GET" case head = "HEAD" case post = "POST" case put = "PUT" case patch = "PATCH" case delete = "DELETE" case trace = "TRACE" case connect = "CONNECT" } // MARK: - /// A dictionary of parameters to apply to a `URLRequest`. public typealias Parameters = [String: Any] /// A type used to define how a set of parameters are applied to a `URLRequest`. public protocol ParameterEncoding { /// Creates a URL request by encoding parameters and applying them onto an existing request. /// /// - parameter urlRequest: The request to have parameters applied. /// - parameter parameters: The parameters to apply. /// /// - throws: An `AFError.parameterEncodingFailed` error if encoding fails. /// /// - returns: The encoded request. func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest } // MARK: - /// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP /// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as /// the HTTP body depends on the destination of the encoding. /// /// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to /// `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification for how to encode /// collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending /// the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`). public struct URLEncoding: ParameterEncoding { // MARK: Helper Types /// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the /// resulting URL request. /// /// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` /// requests and sets as the HTTP body for requests with any other HTTP method. /// - queryString: Sets or appends encoded query string result to existing query string. /// - httpBody: Sets encoded query string result as the HTTP body of the URL request. public enum Destination { case methodDependent, queryString, httpBody } // MARK: Properties /// Returns a default `URLEncoding` instance. public static var `default`: URLEncoding { return URLEncoding() } /// Returns a `URLEncoding` instance with a `.methodDependent` destination. public static var methodDependent: URLEncoding { return URLEncoding() } /// Returns a `URLEncoding` instance with a `.queryString` destination. public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) } /// Returns a `URLEncoding` instance with an `.httpBody` destination. public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) } /// The destination defining where the encoded query string is to be applied to the URL request. public let destination: Destination // MARK: Initialization /// Creates a `URLEncoding` instance using the specified destination. /// /// - parameter destination: The destination defining where the encoded query string is to be applied. /// /// - returns: The new `URLEncoding` instance. public init(destination: Destination = .methodDependent) { self.destination = destination } // MARK: Encoding /// Creates a URL request by encoding parameters and applying them onto an existing request. /// /// - parameter urlRequest: The request to have parameters applied. /// - parameter parameters: The parameters to apply. /// /// - throws: An `Error` if the encoding process encounters an error. /// /// - returns: The encoded request. public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { var urlRequest = try urlRequest.asURLRequest() guard let parameters = parameters else { return urlRequest } if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) { guard let url = urlRequest.url else { throw AFError.parameterEncodingFailed(reason: .missingURL) } if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty { let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) urlComponents.percentEncodedQuery = percentEncodedQuery urlRequest.url = urlComponents.url } } else { if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") } urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false) } return urlRequest } /// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. /// /// - parameter key: The key of the query component. /// - parameter value: The value of the query component. /// /// - returns: The percent-escaped, URL encoded query string components. public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] { var components: [(String, String)] = [] if let dictionary = value as? [String: Any] { for (nestedKey, value) in dictionary { components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) } } else if let array = value as? [Any] { for value in array { components += queryComponents(fromKey: "\(key)[]", value: value) } } else if let value = value as? NSNumber { if value.isBool { components.append((escape(key), escape((value.boolValue ? "1" : "0")))) } else { components.append((escape(key), escape("\(value)"))) } } else if let bool = value as? Bool { components.append((escape(key), escape((bool ? "1" : "0")))) } else { components.append((escape(key), escape("\(value)"))) } return components } /// Returns a percent-escaped string following RFC 3986 for a query string key or value. /// /// RFC 3986 states that the following characters are "reserved" characters. /// /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/" /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" /// /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" /// should be percent-escaped in the query string. /// /// - parameter string: The string to be percent-escaped. /// /// - returns: The percent-escaped string. public func escape(_ string: String) -> String { let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 let subDelimitersToEncode = "!$&'()*+,;=" var allowedCharacterSet = CharacterSet.urlQueryAllowed allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") var escaped = "" //========================================================================================================== // // Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few // hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no // longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more // info, please refer to: // // - https://github.com/Alamofire/Alamofire/issues/206 // //========================================================================================================== if #available(iOS 8.3, *) { escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string } else { let batchSize = 50 var index = string.startIndex while index != string.endIndex { let startIndex = index let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex let range = startIndex.. String { var components: [(String, String)] = [] for key in parameters.keys.sorted(by: <) { let value = parameters[key]! components += queryComponents(fromKey: key, value: value) } return components.map { "\($0)=\($1)" }.joined(separator: "&") } private func encodesParametersInURL(with method: HTTPMethod) -> Bool { switch destination { case .queryString: return true case .httpBody: return false default: break } switch method { case .get, .head, .delete: return true default: return false } } } // MARK: - /// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the /// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. public struct JSONEncoding: ParameterEncoding { // MARK: Properties /// Returns a `JSONEncoding` instance with default writing options. public static var `default`: JSONEncoding { return JSONEncoding() } /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options. public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) } /// The options for writing the parameters as JSON data. public let options: JSONSerialization.WritingOptions // MARK: Initialization /// Creates a `JSONEncoding` instance using the specified options. /// /// - parameter options: The options for writing the parameters as JSON data. /// /// - returns: The new `JSONEncoding` instance. public init(options: JSONSerialization.WritingOptions = []) { self.options = options } // MARK: Encoding /// Creates a URL request by encoding parameters and applying them onto an existing request. /// /// - parameter urlRequest: The request to have parameters applied. /// - parameter parameters: The parameters to apply. /// /// - throws: An `Error` if the encoding process encounters an error. /// /// - returns: The encoded request. public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { var urlRequest = try urlRequest.asURLRequest() guard let parameters = parameters else { return urlRequest } do { let data = try JSONSerialization.data(withJSONObject: parameters, options: options) if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") } urlRequest.httpBody = data } catch { throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) } return urlRequest } /// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body. /// /// - parameter urlRequest: The request to apply the JSON object to. /// - parameter jsonObject: The JSON object to apply to the request. /// /// - throws: An `Error` if the encoding process encounters an error. /// /// - returns: The encoded request. public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest { var urlRequest = try urlRequest.asURLRequest() guard let jsonObject = jsonObject else { return urlRequest } do { let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options) if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") } urlRequest.httpBody = data } catch { throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error)) } return urlRequest } } // MARK: - /// Uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the /// associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header /// field of an encoded request is set to `application/x-plist`. public struct PropertyListEncoding: ParameterEncoding { // MARK: Properties /// Returns a default `PropertyListEncoding` instance. public static var `default`: PropertyListEncoding { return PropertyListEncoding() } /// Returns a `PropertyListEncoding` instance with xml formatting and default writing options. public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) } /// Returns a `PropertyListEncoding` instance with binary formatting and default writing options. public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) } /// The property list serialization format. public let format: PropertyListSerialization.PropertyListFormat /// The options for writing the parameters as plist data. public let options: PropertyListSerialization.WriteOptions // MARK: Initialization /// Creates a `PropertyListEncoding` instance using the specified format and options. /// /// - parameter format: The property list serialization format. /// - parameter options: The options for writing the parameters as plist data. /// /// - returns: The new `PropertyListEncoding` instance. public init( format: PropertyListSerialization.PropertyListFormat = .xml, options: PropertyListSerialization.WriteOptions = 0) { self.format = format self.options = options } // MARK: Encoding /// Creates a URL request by encoding parameters and applying them onto an existing request. /// /// - parameter urlRequest: The request to have parameters applied. /// - parameter parameters: The parameters to apply. /// /// - throws: An `Error` if the encoding process encounters an error. /// /// - returns: The encoded request. public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { var urlRequest = try urlRequest.asURLRequest() guard let parameters = parameters else { return urlRequest } do { let data = try PropertyListSerialization.data( fromPropertyList: parameters, format: format, options: options ) if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") } urlRequest.httpBody = data } catch { throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error)) } return urlRequest } } // MARK: - extension NSNumber { fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) } } ================================================ FILE: Pods/Alamofire/Source/Request.swift ================================================ // // Request.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary. public protocol RequestAdapter { /// Inspects and adapts the specified `URLRequest` in some manner if necessary and returns the result. /// /// - parameter urlRequest: The URL request to adapt. /// /// - throws: An `Error` if the adaptation encounters an error. /// /// - returns: The adapted `URLRequest`. func adapt(_ urlRequest: URLRequest) throws -> URLRequest } // MARK: - /// A closure executed when the `RequestRetrier` determines whether a `Request` should be retried or not. public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void /// A type that determines whether a request should be retried after being executed by the specified session manager /// and encountering an error. public protocol RequestRetrier { /// Determines whether the `Request` should be retried by calling the `completion` closure. /// /// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs /// to be retried. The one requirement is that the completion closure is called to ensure the request is properly /// cleaned up after. /// /// - parameter manager: The session manager the request was executed on. /// - parameter request: The request that failed due to the encountered error. /// - parameter error: The error encountered when executing the request. /// - parameter completion: The completion closure to be executed when retry decision has been determined. func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) } // MARK: - protocol TaskConvertible { func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask } /// A dictionary of headers to apply to a `URLRequest`. public typealias HTTPHeaders = [String: String] // MARK: - /// Responsible for sending a request and receiving the response and associated data from the server, as well as /// managing its underlying `URLSessionTask`. open class Request { // MARK: Helper Types /// A closure executed when monitoring upload or download progress of a request. public typealias ProgressHandler = (Progress) -> Void enum RequestTask { case data(TaskConvertible?, URLSessionTask?) case download(TaskConvertible?, URLSessionTask?) case upload(TaskConvertible?, URLSessionTask?) case stream(TaskConvertible?, URLSessionTask?) } // MARK: Properties /// The delegate for the underlying task. open internal(set) var delegate: TaskDelegate { get { taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() } return taskDelegate } set { taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() } taskDelegate = newValue } } /// The underlying task. open var task: URLSessionTask? { return delegate.task } /// The session belonging to the underlying task. open let session: URLSession /// The request sent or to be sent to the server. open var request: URLRequest? { return task?.originalRequest } /// The response received from the server, if any. open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse } /// The number of times the request has been retried. open internal(set) var retryCount: UInt = 0 let originalTask: TaskConvertible? var startTime: CFAbsoluteTime? var endTime: CFAbsoluteTime? var validations: [() -> Void] = [] private var taskDelegate: TaskDelegate private var taskDelegateLock = NSLock() // MARK: Lifecycle init(session: URLSession, requestTask: RequestTask, error: Error? = nil) { self.session = session switch requestTask { case .data(let originalTask, let task): taskDelegate = DataTaskDelegate(task: task) self.originalTask = originalTask case .download(let originalTask, let task): taskDelegate = DownloadTaskDelegate(task: task) self.originalTask = originalTask case .upload(let originalTask, let task): taskDelegate = UploadTaskDelegate(task: task) self.originalTask = originalTask case .stream(let originalTask, let task): taskDelegate = TaskDelegate(task: task) self.originalTask = originalTask } delegate.error = error delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() } } // MARK: Authentication /// Associates an HTTP Basic credential with the request. /// /// - parameter user: The user. /// - parameter password: The password. /// - parameter persistence: The URL credential persistence. `.ForSession` by default. /// /// - returns: The request. @discardableResult open func authenticate( user: String, password: String, persistence: URLCredential.Persistence = .forSession) -> Self { let credential = URLCredential(user: user, password: password, persistence: persistence) return authenticate(usingCredential: credential) } /// Associates a specified credential with the request. /// /// - parameter credential: The credential. /// /// - returns: The request. @discardableResult open func authenticate(usingCredential credential: URLCredential) -> Self { delegate.credential = credential return self } /// Returns a base64 encoded basic authentication credential as an authorization header tuple. /// /// - parameter user: The user. /// - parameter password: The password. /// /// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise. open static func authorizationHeader(user: String, password: String) -> (key: String, value: String)? { guard let data = "\(user):\(password)".data(using: .utf8) else { return nil } let credential = data.base64EncodedString(options: []) return (key: "Authorization", value: "Basic \(credential)") } // MARK: State /// Resumes the request. open func resume() { guard let task = task else { delegate.queue.isSuspended = false ; return } if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() } task.resume() NotificationCenter.default.post( name: Notification.Name.Task.DidResume, object: self, userInfo: [Notification.Key.Task: task] ) } /// Suspends the request. open func suspend() { guard let task = task else { return } task.suspend() NotificationCenter.default.post( name: Notification.Name.Task.DidSuspend, object: self, userInfo: [Notification.Key.Task: task] ) } /// Cancels the request. open func cancel() { guard let task = task else { return } task.cancel() NotificationCenter.default.post( name: Notification.Name.Task.DidCancel, object: self, userInfo: [Notification.Key.Task: task] ) } } // MARK: - CustomStringConvertible extension Request: CustomStringConvertible { /// The textual representation used when written to an output stream, which includes the HTTP method and URL, as /// well as the response status code if a response has been received. open var description: String { var components: [String] = [] if let HTTPMethod = request?.httpMethod { components.append(HTTPMethod) } if let urlString = request?.url?.absoluteString { components.append(urlString) } if let response = response { components.append("(\(response.statusCode))") } return components.joined(separator: " ") } } // MARK: - CustomDebugStringConvertible extension Request: CustomDebugStringConvertible { /// The textual representation used when written to an output stream, in the form of a cURL command. open var debugDescription: String { return cURLRepresentation() } func cURLRepresentation() -> String { var components = ["$ curl -v"] guard let request = self.request, let url = request.url, let host = url.host else { return "$ curl command could not be created" } if let httpMethod = request.httpMethod, httpMethod != "GET" { components.append("-X \(httpMethod)") } if let credentialStorage = self.session.configuration.urlCredentialStorage { let protectionSpace = URLProtectionSpace( host: host, port: url.port ?? 0, protocol: url.scheme, realm: host, authenticationMethod: NSURLAuthenticationMethodHTTPBasic ) if let credentials = credentialStorage.credentials(for: protectionSpace)?.values { for credential in credentials { guard let user = credential.user, let password = credential.password else { continue } components.append("-u \(user):\(password)") } } else { if let credential = delegate.credential, let user = credential.user, let password = credential.password { components.append("-u \(user):\(password)") } } } if session.configuration.httpShouldSetCookies { if let cookieStorage = session.configuration.httpCookieStorage, let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty { let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);" } #if swift(>=3.2) components.append("-b \"\(string[.. URLSessionTask { do { let urlRequest = try self.urlRequest.adapt(using: adapter) return queue.sync { session.dataTask(with: urlRequest) } } catch { throw AdaptError(error: error) } } } // MARK: Properties /// The request sent or to be sent to the server. open override var request: URLRequest? { if let request = super.request { return request } if let requestable = originalTask as? Requestable { return requestable.urlRequest } return nil } /// The progress of fetching the response data from the server for the request. open var progress: Progress { return dataDelegate.progress } var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate } // MARK: Stream /// Sets a closure to be called periodically during the lifecycle of the request as data is read from the server. /// /// This closure returns the bytes most recently received from the server, not including data from previous calls. /// If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is /// also important to note that the server data in any `Response` object will be `nil`. /// /// - parameter closure: The code to be executed periodically during the lifecycle of the request. /// /// - returns: The request. @discardableResult open func stream(closure: ((Data) -> Void)? = nil) -> Self { dataDelegate.dataStream = closure return self } // MARK: Progress /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server. /// /// - parameter queue: The dispatch queue to execute the closure on. /// - parameter closure: The code to be executed periodically as data is read from the server. /// /// - returns: The request. @discardableResult open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self { dataDelegate.progressHandler = (closure, queue) return self } } // MARK: - /// Specific type of `Request` that manages an underlying `URLSessionDownloadTask`. open class DownloadRequest: Request { // MARK: Helper Types /// A collection of options to be executed prior to moving a downloaded file from the temporary URL to the /// destination URL. public struct DownloadOptions: OptionSet { /// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol. public let rawValue: UInt /// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified. public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0) /// A `DownloadOptions` flag that removes a previous file from the destination URL if specified. public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1) /// Creates a `DownloadFileDestinationOptions` instance with the specified raw value. /// /// - parameter rawValue: The raw bitmask value for the option. /// /// - returns: A new log level instance. public init(rawValue: UInt) { self.rawValue = rawValue } } /// A closure executed once a download request has successfully completed in order to determine where to move the /// temporary file written to during the download process. The closure takes two arguments: the temporary file URL /// and the URL response, and returns a two arguments: the file URL where the temporary file should be moved and /// the options defining how the file should be moved. public typealias DownloadFileDestination = ( _ temporaryURL: URL, _ response: HTTPURLResponse) -> (destinationURL: URL, options: DownloadOptions) enum Downloadable: TaskConvertible { case request(URLRequest) case resumeData(Data) func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask { do { let task: URLSessionTask switch self { case let .request(urlRequest): let urlRequest = try urlRequest.adapt(using: adapter) task = queue.sync { session.downloadTask(with: urlRequest) } case let .resumeData(resumeData): task = queue.sync { session.downloadTask(withResumeData: resumeData) } } return task } catch { throw AdaptError(error: error) } } } // MARK: Properties /// The request sent or to be sent to the server. open override var request: URLRequest? { if let request = super.request { return request } if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable { return urlRequest } return nil } /// The resume data of the underlying download task if available after a failure. open var resumeData: Data? { return downloadDelegate.resumeData } /// The progress of downloading the response data from the server for the request. open var progress: Progress { return downloadDelegate.progress } var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate } // MARK: State /// Cancels the request. open override func cancel() { downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 } NotificationCenter.default.post( name: Notification.Name.Task.DidCancel, object: self, userInfo: [Notification.Key.Task: task as Any] ) } // MARK: Progress /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server. /// /// - parameter queue: The dispatch queue to execute the closure on. /// - parameter closure: The code to be executed periodically as data is read from the server. /// /// - returns: The request. @discardableResult open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self { downloadDelegate.progressHandler = (closure, queue) return self } // MARK: Destination /// Creates a download file destination closure which uses the default file manager to move the temporary file to a /// file URL in the first available directory with the specified search path directory and search path domain mask. /// /// - parameter directory: The search path directory. `.DocumentDirectory` by default. /// - parameter domain: The search path domain mask. `.UserDomainMask` by default. /// /// - returns: A download file destination closure. open class func suggestedDownloadDestination( for directory: FileManager.SearchPathDirectory = .documentDirectory, in domain: FileManager.SearchPathDomainMask = .userDomainMask) -> DownloadFileDestination { return { temporaryURL, response in let directoryURLs = FileManager.default.urls(for: directory, in: domain) if !directoryURLs.isEmpty { return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), []) } return (temporaryURL, []) } } } // MARK: - /// Specific type of `Request` that manages an underlying `URLSessionUploadTask`. open class UploadRequest: DataRequest { // MARK: Helper Types enum Uploadable: TaskConvertible { case data(Data, URLRequest) case file(URL, URLRequest) case stream(InputStream, URLRequest) func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask { do { let task: URLSessionTask switch self { case let .data(data, urlRequest): let urlRequest = try urlRequest.adapt(using: adapter) task = queue.sync { session.uploadTask(with: urlRequest, from: data) } case let .file(url, urlRequest): let urlRequest = try urlRequest.adapt(using: adapter) task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) } case let .stream(_, urlRequest): let urlRequest = try urlRequest.adapt(using: adapter) task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) } } return task } catch { throw AdaptError(error: error) } } } // MARK: Properties /// The request sent or to be sent to the server. open override var request: URLRequest? { if let request = super.request { return request } guard let uploadable = originalTask as? Uploadable else { return nil } switch uploadable { case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest): return urlRequest } } /// The progress of uploading the payload to the server for the upload request. open var uploadProgress: Progress { return uploadDelegate.uploadProgress } var uploadDelegate: UploadTaskDelegate { return delegate as! UploadTaskDelegate } // MARK: Upload Progress /// Sets a closure to be called periodically during the lifecycle of the `UploadRequest` as data is sent to /// the server. /// /// After the data is sent to the server, the `progress(queue:closure:)` APIs can be used to monitor the progress /// of data being read from the server. /// /// - parameter queue: The dispatch queue to execute the closure on. /// - parameter closure: The code to be executed periodically as data is sent to the server. /// /// - returns: The request. @discardableResult open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self { uploadDelegate.uploadProgressHandler = (closure, queue) return self } } // MARK: - #if !os(watchOS) /// Specific type of `Request` that manages an underlying `URLSessionStreamTask`. @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) open class StreamRequest: Request { enum Streamable: TaskConvertible { case stream(hostName: String, port: Int) case netService(NetService) func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask { let task: URLSessionTask switch self { case let .stream(hostName, port): task = queue.sync { session.streamTask(withHostName: hostName, port: port) } case let .netService(netService): task = queue.sync { session.streamTask(with: netService) } } return task } } } #endif ================================================ FILE: Pods/Alamofire/Source/Response.swift ================================================ // // Response.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// Used to store all data associated with an non-serialized response of a data or upload request. public struct DefaultDataResponse { /// The URL request sent to the server. public let request: URLRequest? /// The server's response to the URL request. public let response: HTTPURLResponse? /// The data returned by the server. public let data: Data? /// The error encountered while executing or validating the request. public let error: Error? /// The timeline of the complete lifecycle of the request. public let timeline: Timeline var _metrics: AnyObject? /// Creates a `DefaultDataResponse` instance from the specified parameters. /// /// - Parameters: /// - request: The URL request sent to the server. /// - response: The server's response to the URL request. /// - data: The data returned by the server. /// - error: The error encountered while executing or validating the request. /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default. /// - metrics: The task metrics containing the request / response statistics. `nil` by default. public init( request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?, timeline: Timeline = Timeline(), metrics: AnyObject? = nil) { self.request = request self.response = response self.data = data self.error = error self.timeline = timeline } } // MARK: - /// Used to store all data associated with a serialized response of a data or upload request. public struct DataResponse { /// The URL request sent to the server. public let request: URLRequest? /// The server's response to the URL request. public let response: HTTPURLResponse? /// The data returned by the server. public let data: Data? /// The result of response serialization. public let result: Result /// The timeline of the complete lifecycle of the request. public let timeline: Timeline /// Returns the associated value of the result if it is a success, `nil` otherwise. public var value: Value? { return result.value } /// Returns the associated error value if the result if it is a failure, `nil` otherwise. public var error: Error? { return result.error } var _metrics: AnyObject? /// Creates a `DataResponse` instance with the specified parameters derived from response serialization. /// /// - parameter request: The URL request sent to the server. /// - parameter response: The server's response to the URL request. /// - parameter data: The data returned by the server. /// - parameter result: The result of response serialization. /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. /// /// - returns: The new `DataResponse` instance. public init( request: URLRequest?, response: HTTPURLResponse?, data: Data?, result: Result, timeline: Timeline = Timeline()) { self.request = request self.response = response self.data = data self.result = result self.timeline = timeline } } // MARK: - extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible { /// The textual representation used when written to an output stream, which includes whether the result was a /// success or failure. public var description: String { return result.debugDescription } /// The debug textual representation used when written to an output stream, which includes the URL request, the URL /// response, the server data, the response serialization result and the timeline. public var debugDescription: String { var output: [String] = [] output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil") output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") output.append("[Data]: \(data?.count ?? 0) bytes") output.append("[Result]: \(result.debugDescription)") output.append("[Timeline]: \(timeline.debugDescription)") return output.joined(separator: "\n") } } // MARK: - extension DataResponse { /// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped /// result value as a parameter. /// /// Use the `map` method with a closure that does not throw. For example: /// /// let possibleData: DataResponse = ... /// let possibleInt = possibleData.map { $0.count } /// /// - parameter transform: A closure that takes the success value of the instance's result. /// /// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's /// result is a failure, returns a response wrapping the same failure. public func map(_ transform: (Value) -> T) -> DataResponse { var response = DataResponse( request: request, response: self.response, data: data, result: result.map(transform), timeline: timeline ) response._metrics = _metrics return response } /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result /// value as a parameter. /// /// Use the `flatMap` method with a closure that may throw an error. For example: /// /// let possibleData: DataResponse = ... /// let possibleObject = possibleData.flatMap { /// try JSONSerialization.jsonObject(with: $0) /// } /// /// - parameter transform: A closure that takes the success value of the instance's result. /// /// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's /// result is a failure, returns the same failure. public func flatMap(_ transform: (Value) throws -> T) -> DataResponse { var response = DataResponse( request: request, response: self.response, data: data, result: result.flatMap(transform), timeline: timeline ) response._metrics = _metrics return response } } // MARK: - /// Used to store all data associated with an non-serialized response of a download request. public struct DefaultDownloadResponse { /// The URL request sent to the server. public let request: URLRequest? /// The server's response to the URL request. public let response: HTTPURLResponse? /// The temporary destination URL of the data returned from the server. public let temporaryURL: URL? /// The final destination URL of the data returned from the server if it was moved. public let destinationURL: URL? /// The resume data generated if the request was cancelled. public let resumeData: Data? /// The error encountered while executing or validating the request. public let error: Error? /// The timeline of the complete lifecycle of the request. public let timeline: Timeline var _metrics: AnyObject? /// Creates a `DefaultDownloadResponse` instance from the specified parameters. /// /// - Parameters: /// - request: The URL request sent to the server. /// - response: The server's response to the URL request. /// - temporaryURL: The temporary destination URL of the data returned from the server. /// - destinationURL: The final destination URL of the data returned from the server if it was moved. /// - resumeData: The resume data generated if the request was cancelled. /// - error: The error encountered while executing or validating the request. /// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default. /// - metrics: The task metrics containing the request / response statistics. `nil` by default. public init( request: URLRequest?, response: HTTPURLResponse?, temporaryURL: URL?, destinationURL: URL?, resumeData: Data?, error: Error?, timeline: Timeline = Timeline(), metrics: AnyObject? = nil) { self.request = request self.response = response self.temporaryURL = temporaryURL self.destinationURL = destinationURL self.resumeData = resumeData self.error = error self.timeline = timeline } } // MARK: - /// Used to store all data associated with a serialized response of a download request. public struct DownloadResponse { /// The URL request sent to the server. public let request: URLRequest? /// The server's response to the URL request. public let response: HTTPURLResponse? /// The temporary destination URL of the data returned from the server. public let temporaryURL: URL? /// The final destination URL of the data returned from the server if it was moved. public let destinationURL: URL? /// The resume data generated if the request was cancelled. public let resumeData: Data? /// The result of response serialization. public let result: Result /// The timeline of the complete lifecycle of the request. public let timeline: Timeline /// Returns the associated value of the result if it is a success, `nil` otherwise. public var value: Value? { return result.value } /// Returns the associated error value if the result if it is a failure, `nil` otherwise. public var error: Error? { return result.error } var _metrics: AnyObject? /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization. /// /// - parameter request: The URL request sent to the server. /// - parameter response: The server's response to the URL request. /// - parameter temporaryURL: The temporary destination URL of the data returned from the server. /// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved. /// - parameter resumeData: The resume data generated if the request was cancelled. /// - parameter result: The result of response serialization. /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`. /// /// - returns: The new `DownloadResponse` instance. public init( request: URLRequest?, response: HTTPURLResponse?, temporaryURL: URL?, destinationURL: URL?, resumeData: Data?, result: Result, timeline: Timeline = Timeline()) { self.request = request self.response = response self.temporaryURL = temporaryURL self.destinationURL = destinationURL self.resumeData = resumeData self.result = result self.timeline = timeline } } // MARK: - extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible { /// The textual representation used when written to an output stream, which includes whether the result was a /// success or failure. public var description: String { return result.debugDescription } /// The debug textual representation used when written to an output stream, which includes the URL request, the URL /// response, the temporary and destination URLs, the resume data, the response serialization result and the /// timeline. public var debugDescription: String { var output: [String] = [] output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil") output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil") output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")") output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")") output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes") output.append("[Result]: \(result.debugDescription)") output.append("[Timeline]: \(timeline.debugDescription)") return output.joined(separator: "\n") } } // MARK: - extension DownloadResponse { /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped /// result value as a parameter. /// /// Use the `map` method with a closure that does not throw. For example: /// /// let possibleData: DownloadResponse = ... /// let possibleInt = possibleData.map { $0.count } /// /// - parameter transform: A closure that takes the success value of the instance's result. /// /// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's /// result is a failure, returns a response wrapping the same failure. public func map(_ transform: (Value) -> T) -> DownloadResponse { var response = DownloadResponse( request: request, response: self.response, temporaryURL: temporaryURL, destinationURL: destinationURL, resumeData: resumeData, result: result.map(transform), timeline: timeline ) response._metrics = _metrics return response } /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped /// result value as a parameter. /// /// Use the `flatMap` method with a closure that may throw an error. For example: /// /// let possibleData: DownloadResponse = ... /// let possibleObject = possibleData.flatMap { /// try JSONSerialization.jsonObject(with: $0) /// } /// /// - parameter transform: A closure that takes the success value of the instance's result. /// /// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this /// instance's result is a failure, returns the same failure. public func flatMap(_ transform: (Value) throws -> T) -> DownloadResponse { var response = DownloadResponse( request: request, response: self.response, temporaryURL: temporaryURL, destinationURL: destinationURL, resumeData: resumeData, result: result.flatMap(transform), timeline: timeline ) response._metrics = _metrics return response } } // MARK: - protocol Response { /// The task metrics containing the request / response statistics. var _metrics: AnyObject? { get set } mutating func add(_ metrics: AnyObject?) } extension Response { mutating func add(_ metrics: AnyObject?) { #if !os(watchOS) guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return } guard let metrics = metrics as? URLSessionTaskMetrics else { return } _metrics = metrics #endif } } // MARK: - @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) extension DefaultDataResponse: Response { #if !os(watchOS) /// The task metrics containing the request / response statistics. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } #endif } @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) extension DataResponse: Response { #if !os(watchOS) /// The task metrics containing the request / response statistics. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } #endif } @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) extension DefaultDownloadResponse: Response { #if !os(watchOS) /// The task metrics containing the request / response statistics. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } #endif } @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) extension DownloadResponse: Response { #if !os(watchOS) /// The task metrics containing the request / response statistics. public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics } #endif } ================================================ FILE: Pods/Alamofire/Source/ResponseSerialization.swift ================================================ // // ResponseSerialization.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// The type in which all data response serializers must conform to in order to serialize a response. public protocol DataResponseSerializerProtocol { /// The type of serialized object to be created by this `DataResponseSerializerType`. associatedtype SerializedObject /// A closure used by response handlers that takes a request, response, data and error and returns a result. var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result { get } } // MARK: - /// A generic `DataResponseSerializerType` used to serialize a request, response, and data into a serialized object. public struct DataResponseSerializer: DataResponseSerializerProtocol { /// The type of serialized object to be created by this `DataResponseSerializer`. public typealias SerializedObject = Value /// A closure used by response handlers that takes a request, response, data and error and returns a result. public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result /// Initializes the `ResponseSerializer` instance with the given serialize response closure. /// /// - parameter serializeResponse: The closure used to serialize the response. /// /// - returns: The new generic response serializer instance. public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result) { self.serializeResponse = serializeResponse } } // MARK: - /// The type in which all download response serializers must conform to in order to serialize a response. public protocol DownloadResponseSerializerProtocol { /// The type of serialized object to be created by this `DownloadResponseSerializerType`. associatedtype SerializedObject /// A closure used by response handlers that takes a request, response, url and error and returns a result. var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result { get } } // MARK: - /// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object. public struct DownloadResponseSerializer: DownloadResponseSerializerProtocol { /// The type of serialized object to be created by this `DownloadResponseSerializer`. public typealias SerializedObject = Value /// A closure used by response handlers that takes a request, response, url and error and returns a result. public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result /// Initializes the `ResponseSerializer` instance with the given serialize response closure. /// /// - parameter serializeResponse: The closure used to serialize the response. /// /// - returns: The new generic response serializer instance. public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result) { self.serializeResponse = serializeResponse } } // MARK: - Timeline extension Request { var timeline: Timeline { let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent() let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent() let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime return Timeline( requestStartTime: requestStartTime, initialResponseTime: initialResponseTime, requestCompletedTime: requestCompletedTime, serializationCompletedTime: CFAbsoluteTimeGetCurrent() ) } } // MARK: - Default extension DataRequest { /// Adds a handler to be called once the request has finished. /// /// - parameter queue: The queue on which the completion handler is dispatched. /// - parameter completionHandler: The code to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self { delegate.queue.addOperation { (queue ?? DispatchQueue.main).async { var dataResponse = DefaultDataResponse( request: self.request, response: self.response, data: self.delegate.data, error: self.delegate.error, timeline: self.timeline ) dataResponse.add(self.delegate.metrics) completionHandler(dataResponse) } } return self } /// Adds a handler to be called once the request has finished. /// /// - parameter queue: The queue on which the completion handler is dispatched. /// - parameter responseSerializer: The response serializer responsible for serializing the request, response, /// and data. /// - parameter completionHandler: The code to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func response( queue: DispatchQueue? = nil, responseSerializer: T, completionHandler: @escaping (DataResponse) -> Void) -> Self { delegate.queue.addOperation { let result = responseSerializer.serializeResponse( self.request, self.response, self.delegate.data, self.delegate.error ) var dataResponse = DataResponse( request: self.request, response: self.response, data: self.delegate.data, result: result, timeline: self.timeline ) dataResponse.add(self.delegate.metrics) (queue ?? DispatchQueue.main).async { completionHandler(dataResponse) } } return self } } extension DownloadRequest { /// Adds a handler to be called once the request has finished. /// /// - parameter queue: The queue on which the completion handler is dispatched. /// - parameter completionHandler: The code to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func response( queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDownloadResponse) -> Void) -> Self { delegate.queue.addOperation { (queue ?? DispatchQueue.main).async { var downloadResponse = DefaultDownloadResponse( request: self.request, response: self.response, temporaryURL: self.downloadDelegate.temporaryURL, destinationURL: self.downloadDelegate.destinationURL, resumeData: self.downloadDelegate.resumeData, error: self.downloadDelegate.error, timeline: self.timeline ) downloadResponse.add(self.delegate.metrics) completionHandler(downloadResponse) } } return self } /// Adds a handler to be called once the request has finished. /// /// - parameter queue: The queue on which the completion handler is dispatched. /// - parameter responseSerializer: The response serializer responsible for serializing the request, response, /// and data contained in the destination url. /// - parameter completionHandler: The code to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func response( queue: DispatchQueue? = nil, responseSerializer: T, completionHandler: @escaping (DownloadResponse) -> Void) -> Self { delegate.queue.addOperation { let result = responseSerializer.serializeResponse( self.request, self.response, self.downloadDelegate.fileURL, self.downloadDelegate.error ) var downloadResponse = DownloadResponse( request: self.request, response: self.response, temporaryURL: self.downloadDelegate.temporaryURL, destinationURL: self.downloadDelegate.destinationURL, resumeData: self.downloadDelegate.resumeData, result: result, timeline: self.timeline ) downloadResponse.add(self.delegate.metrics) (queue ?? DispatchQueue.main).async { completionHandler(downloadResponse) } } return self } } // MARK: - Data extension Request { /// Returns a result data type that contains the response data as-is. /// /// - parameter response: The response from the server. /// - parameter data: The data returned from the server. /// - parameter error: The error already encountered if it exists. /// /// - returns: The result data type. public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result { guard error == nil else { return .failure(error!) } if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) } guard let validData = data else { return .failure(AFError.responseSerializationFailed(reason: .inputDataNil)) } return .success(validData) } } extension DataRequest { /// Creates a response serializer that returns the associated data as-is. /// /// - returns: A data response serializer. public static func dataResponseSerializer() -> DataResponseSerializer { return DataResponseSerializer { _, response, data, error in return Request.serializeResponseData(response: response, data: data, error: error) } } /// Adds a handler to be called once the request has finished. /// /// - parameter completionHandler: The code to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responseData( queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.dataResponseSerializer(), completionHandler: completionHandler ) } } extension DownloadRequest { /// Creates a response serializer that returns the associated data as-is. /// /// - returns: A data response serializer. public static func dataResponseSerializer() -> DownloadResponseSerializer { return DownloadResponseSerializer { _, response, fileURL, error in guard error == nil else { return .failure(error!) } guard let fileURL = fileURL else { return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) } do { let data = try Data(contentsOf: fileURL) return Request.serializeResponseData(response: response, data: data, error: error) } catch { return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) } } } /// Adds a handler to be called once the request has finished. /// /// - parameter completionHandler: The code to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responseData( queue: DispatchQueue? = nil, completionHandler: @escaping (DownloadResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DownloadRequest.dataResponseSerializer(), completionHandler: completionHandler ) } } // MARK: - String extension Request { /// Returns a result string type initialized from the response data with the specified string encoding. /// /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server /// response, falling back to the default HTTP default character set, ISO-8859-1. /// - parameter response: The response from the server. /// - parameter data: The data returned from the server. /// - parameter error: The error already encountered if it exists. /// /// - returns: The result data type. public static func serializeResponseString( encoding: String.Encoding?, response: HTTPURLResponse?, data: Data?, error: Error?) -> Result { guard error == nil else { return .failure(error!) } if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") } guard let validData = data else { return .failure(AFError.responseSerializationFailed(reason: .inputDataNil)) } var convertedEncoding = encoding if let encodingName = response?.textEncodingName as CFString!, convertedEncoding == nil { convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding( CFStringConvertIANACharSetNameToEncoding(encodingName)) ) } let actualEncoding = convertedEncoding ?? String.Encoding.isoLatin1 if let string = String(data: validData, encoding: actualEncoding) { return .success(string) } else { return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding))) } } } extension DataRequest { /// Creates a response serializer that returns a result string type initialized from the response data with /// the specified string encoding. /// /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server /// response, falling back to the default HTTP default character set, ISO-8859-1. /// /// - returns: A string response serializer. public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DataResponseSerializer { return DataResponseSerializer { _, response, data, error in return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error) } } /// Adds a handler to be called once the request has finished. /// /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the /// server response, falling back to the default HTTP default character set, /// ISO-8859-1. /// - parameter completionHandler: A closure to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responseString( queue: DispatchQueue? = nil, encoding: String.Encoding? = nil, completionHandler: @escaping (DataResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding), completionHandler: completionHandler ) } } extension DownloadRequest { /// Creates a response serializer that returns a result string type initialized from the response data with /// the specified string encoding. /// /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server /// response, falling back to the default HTTP default character set, ISO-8859-1. /// /// - returns: A string response serializer. public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DownloadResponseSerializer { return DownloadResponseSerializer { _, response, fileURL, error in guard error == nil else { return .failure(error!) } guard let fileURL = fileURL else { return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) } do { let data = try Data(contentsOf: fileURL) return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error) } catch { return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) } } } /// Adds a handler to be called once the request has finished. /// /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the /// server response, falling back to the default HTTP default character set, /// ISO-8859-1. /// - parameter completionHandler: A closure to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responseString( queue: DispatchQueue? = nil, encoding: String.Encoding? = nil, completionHandler: @escaping (DownloadResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DownloadRequest.stringResponseSerializer(encoding: encoding), completionHandler: completionHandler ) } } // MARK: - JSON extension Request { /// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization` /// with the specified reading options. /// /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. /// - parameter response: The response from the server. /// - parameter data: The data returned from the server. /// - parameter error: The error already encountered if it exists. /// /// - returns: The result data type. public static func serializeResponseJSON( options: JSONSerialization.ReadingOptions, response: HTTPURLResponse?, data: Data?, error: Error?) -> Result { guard error == nil else { return .failure(error!) } if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) } guard let validData = data, validData.count > 0 else { return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)) } do { let json = try JSONSerialization.jsonObject(with: validData, options: options) return .success(json) } catch { return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))) } } } extension DataRequest { /// Creates a response serializer that returns a JSON object result type constructed from the response data using /// `JSONSerialization` with the specified reading options. /// /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. /// /// - returns: A JSON object response serializer. public static func jsonResponseSerializer( options: JSONSerialization.ReadingOptions = .allowFragments) -> DataResponseSerializer { return DataResponseSerializer { _, response, data, error in return Request.serializeResponseJSON(options: options, response: response, data: data, error: error) } } /// Adds a handler to be called once the request has finished. /// /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. /// - parameter completionHandler: A closure to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responseJSON( queue: DispatchQueue? = nil, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (DataResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.jsonResponseSerializer(options: options), completionHandler: completionHandler ) } } extension DownloadRequest { /// Creates a response serializer that returns a JSON object result type constructed from the response data using /// `JSONSerialization` with the specified reading options. /// /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. /// /// - returns: A JSON object response serializer. public static func jsonResponseSerializer( options: JSONSerialization.ReadingOptions = .allowFragments) -> DownloadResponseSerializer { return DownloadResponseSerializer { _, response, fileURL, error in guard error == nil else { return .failure(error!) } guard let fileURL = fileURL else { return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) } do { let data = try Data(contentsOf: fileURL) return Request.serializeResponseJSON(options: options, response: response, data: data, error: error) } catch { return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) } } } /// Adds a handler to be called once the request has finished. /// /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`. /// - parameter completionHandler: A closure to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responseJSON( queue: DispatchQueue? = nil, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (DownloadResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DownloadRequest.jsonResponseSerializer(options: options), completionHandler: completionHandler ) } } // MARK: - Property List extension Request { /// Returns a plist object contained in a result type constructed from the response data using /// `PropertyListSerialization` with the specified reading options. /// /// - parameter options: The property list reading options. Defaults to `[]`. /// - parameter response: The response from the server. /// - parameter data: The data returned from the server. /// - parameter error: The error already encountered if it exists. /// /// - returns: The result data type. public static func serializeResponsePropertyList( options: PropertyListSerialization.ReadOptions, response: HTTPURLResponse?, data: Data?, error: Error?) -> Result { guard error == nil else { return .failure(error!) } if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) } guard let validData = data, validData.count > 0 else { return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)) } do { let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil) return .success(plist) } catch { return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error))) } } } extension DataRequest { /// Creates a response serializer that returns an object constructed from the response data using /// `PropertyListSerialization` with the specified reading options. /// /// - parameter options: The property list reading options. Defaults to `[]`. /// /// - returns: A property list object response serializer. public static func propertyListResponseSerializer( options: PropertyListSerialization.ReadOptions = []) -> DataResponseSerializer { return DataResponseSerializer { _, response, data, error in return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error) } } /// Adds a handler to be called once the request has finished. /// /// - parameter options: The property list reading options. Defaults to `[]`. /// - parameter completionHandler: A closure to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responsePropertyList( queue: DispatchQueue? = nil, options: PropertyListSerialization.ReadOptions = [], completionHandler: @escaping (DataResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.propertyListResponseSerializer(options: options), completionHandler: completionHandler ) } } extension DownloadRequest { /// Creates a response serializer that returns an object constructed from the response data using /// `PropertyListSerialization` with the specified reading options. /// /// - parameter options: The property list reading options. Defaults to `[]`. /// /// - returns: A property list object response serializer. public static func propertyListResponseSerializer( options: PropertyListSerialization.ReadOptions = []) -> DownloadResponseSerializer { return DownloadResponseSerializer { _, response, fileURL, error in guard error == nil else { return .failure(error!) } guard let fileURL = fileURL else { return .failure(AFError.responseSerializationFailed(reason: .inputFileNil)) } do { let data = try Data(contentsOf: fileURL) return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error) } catch { return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))) } } } /// Adds a handler to be called once the request has finished. /// /// - parameter options: The property list reading options. Defaults to `[]`. /// - parameter completionHandler: A closure to be executed once the request has finished. /// /// - returns: The request. @discardableResult public func responsePropertyList( queue: DispatchQueue? = nil, options: PropertyListSerialization.ReadOptions = [], completionHandler: @escaping (DownloadResponse) -> Void) -> Self { return response( queue: queue, responseSerializer: DownloadRequest.propertyListResponseSerializer(options: options), completionHandler: completionHandler ) } } /// A set of HTTP response status code that do not contain response data. private let emptyDataStatusCodes: Set = [204, 205] ================================================ FILE: Pods/Alamofire/Source/Result.swift ================================================ // // Result.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// Used to represent whether a request was successful or encountered an error. /// /// - success: The request and all post processing operations were successful resulting in the serialization of the /// provided associated value. /// /// - failure: The request encountered an error resulting in a failure. The associated values are the original data /// provided by the server as well as the error that caused the failure. public enum Result { case success(Value) case failure(Error) /// Returns `true` if the result is a success, `false` otherwise. public var isSuccess: Bool { switch self { case .success: return true case .failure: return false } } /// Returns `true` if the result is a failure, `false` otherwise. public var isFailure: Bool { return !isSuccess } /// Returns the associated value if the result is a success, `nil` otherwise. public var value: Value? { switch self { case .success(let value): return value case .failure: return nil } } /// Returns the associated error value if the result is a failure, `nil` otherwise. public var error: Error? { switch self { case .success: return nil case .failure(let error): return error } } } // MARK: - CustomStringConvertible extension Result: CustomStringConvertible { /// The textual representation used when written to an output stream, which includes whether the result was a /// success or failure. public var description: String { switch self { case .success: return "SUCCESS" case .failure: return "FAILURE" } } } // MARK: - CustomDebugStringConvertible extension Result: CustomDebugStringConvertible { /// The debug textual representation used when written to an output stream, which includes whether the result was a /// success or failure in addition to the value or error. public var debugDescription: String { switch self { case .success(let value): return "SUCCESS: \(value)" case .failure(let error): return "FAILURE: \(error)" } } } // MARK: - Functional APIs extension Result { /// Creates a `Result` instance from the result of a closure. /// /// A failure result is created when the closure throws, and a success result is created when the closure /// succeeds without throwing an error. /// /// func someString() throws -> String { ... } /// /// let result = Result(value: { /// return try someString() /// }) /// /// // The type of result is Result /// /// The trailing closure syntax is also supported: /// /// let result = Result { try someString() } /// /// - parameter value: The closure to execute and create the result for. public init(value: () throws -> Value) { do { self = try .success(value()) } catch { self = .failure(error) } } /// Returns the success value, or throws the failure error. /// /// let possibleString: Result = .success("success") /// try print(possibleString.unwrap()) /// // Prints "success" /// /// let noString: Result = .failure(error) /// try print(noString.unwrap()) /// // Throws error public func unwrap() throws -> Value { switch self { case .success(let value): return value case .failure(let error): throw error } } /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. /// /// Use the `map` method with a closure that does not throw. For example: /// /// let possibleData: Result = .success(Data()) /// let possibleInt = possibleData.map { $0.count } /// try print(possibleInt.unwrap()) /// // Prints "0" /// /// let noData: Result = .failure(error) /// let noInt = noData.map { $0.count } /// try print(noInt.unwrap()) /// // Throws error /// /// - parameter transform: A closure that takes the success value of the `Result` instance. /// /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the /// same failure. public func map(_ transform: (Value) -> T) -> Result { switch self { case .success(let value): return .success(transform(value)) case .failure(let error): return .failure(error) } } /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. /// /// Use the `flatMap` method with a closure that may throw an error. For example: /// /// let possibleData: Result = .success(Data(...)) /// let possibleObject = possibleData.flatMap { /// try JSONSerialization.jsonObject(with: $0) /// } /// /// - parameter transform: A closure that takes the success value of the instance. /// /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the /// same failure. public func flatMap(_ transform: (Value) throws -> T) -> Result { switch self { case .success(let value): do { return try .success(transform(value)) } catch { return .failure(error) } case .failure(let error): return .failure(error) } } /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. /// /// Use the `mapError` function with a closure that does not throw. For example: /// /// let possibleData: Result = .failure(someError) /// let withMyError: Result = possibleData.mapError { MyError.error($0) } /// /// - Parameter transform: A closure that takes the error of the instance. /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns /// the same instance. public func mapError(_ transform: (Error) -> T) -> Result { switch self { case .failure(let error): return .failure(transform(error)) case .success: return self } } /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. /// /// Use the `flatMapError` function with a closure that may throw an error. For example: /// /// let possibleData: Result = .success(Data(...)) /// let possibleObject = possibleData.flatMapError { /// try someFailableFunction(taking: $0) /// } /// /// - Parameter transform: A throwing closure that takes the error of the instance. /// /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns /// the same instance. public func flatMapError(_ transform: (Error) throws -> T) -> Result { switch self { case .failure(let error): do { return try .failure(transform(error)) } catch { return .failure(error) } case .success: return self } } /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. /// /// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance. /// /// - Parameter closure: A closure that takes the success value of this instance. /// - Returns: This `Result` instance, unmodified. @discardableResult public func withValue(_ closure: (Value) -> Void) -> Result { if case let .success(value) = self { closure(value) } return self } /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. /// /// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance. /// /// - Parameter closure: A closure that takes the success value of this instance. /// - Returns: This `Result` instance, unmodified. @discardableResult public func withError(_ closure: (Error) -> Void) -> Result { if case let .failure(error) = self { closure(error) } return self } /// Evaluates the specified closure when the `Result` is a success. /// /// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance. /// /// - Parameter closure: A `Void` closure. /// - Returns: This `Result` instance, unmodified. @discardableResult public func ifSuccess(_ closure: () -> Void) -> Result { if isSuccess { closure() } return self } /// Evaluates the specified closure when the `Result` is a failure. /// /// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance. /// /// - Parameter closure: A `Void` closure. /// - Returns: This `Result` instance, unmodified. @discardableResult public func ifFailure(_ closure: () -> Void) -> Result { if isFailure { closure() } return self } } ================================================ FILE: Pods/Alamofire/Source/ServerTrustPolicy.swift ================================================ // // ServerTrustPolicy.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. open class ServerTrustPolicyManager { /// The dictionary of policies mapped to a particular host. open let policies: [String: ServerTrustPolicy] /// Initializes the `ServerTrustPolicyManager` instance with the given policies. /// /// Since different servers and web services can have different leaf certificates, intermediate and even root /// certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This /// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key /// pinning for host3 and disabling evaluation for host4. /// /// - parameter policies: A dictionary of all policies mapped to a particular host. /// /// - returns: The new `ServerTrustPolicyManager` instance. public init(policies: [String: ServerTrustPolicy]) { self.policies = policies } /// Returns the `ServerTrustPolicy` for the given host if applicable. /// /// By default, this method will return the policy that perfectly matches the given host. Subclasses could override /// this method and implement more complex mapping implementations such as wildcards. /// /// - parameter host: The host to use when searching for a matching policy. /// /// - returns: The server trust policy for the given host if found. open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? { return policies[host] } } // MARK: - extension URLSession { private struct AssociatedKeys { static var managerKey = "URLSession.ServerTrustPolicyManager" } var serverTrustPolicyManager: ServerTrustPolicyManager? { get { return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager } set (manager) { objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } // MARK: - ServerTrustPolicy /// The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when /// connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust /// with a given set of criteria to determine whether the server trust is valid and the connection should be made. /// /// Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other /// vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged /// to route all communication over an HTTPS connection with pinning enabled. /// /// - performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to /// validate the host provided by the challenge. Applications are encouraged to always /// validate the host in production environments to guarantee the validity of the server's /// certificate chain. /// /// - performRevokedEvaluation: Uses the default and revoked server trust evaluations allowing you to control whether to /// validate the host provided by the challenge as well as specify the revocation flags for /// testing for revoked certificates. Apple platforms did not start testing for revoked /// certificates automatically until iOS 10.1, macOS 10.12 and tvOS 10.1 which is /// demonstrated in our TLS tests. Applications are encouraged to always validate the host /// in production environments to guarantee the validity of the server's certificate chain. /// /// - pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is /// considered valid if one of the pinned certificates match one of the server certificates. /// By validating both the certificate chain and host, certificate pinning provides a very /// secure form of server trust validation mitigating most, if not all, MITM attacks. /// Applications are encouraged to always validate the host and require a valid certificate /// chain in production environments. /// /// - pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered /// valid if one of the pinned public keys match one of the server certificate public keys. /// By validating both the certificate chain and host, public key pinning provides a very /// secure form of server trust validation mitigating most, if not all, MITM attacks. /// Applications are encouraged to always validate the host and require a valid certificate /// chain in production environments. /// /// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid. /// /// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust. public enum ServerTrustPolicy { case performDefaultEvaluation(validateHost: Bool) case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags) case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) case disableEvaluation case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool) // MARK: - Bundle Location /// Returns all certificates within the given bundle with a `.cer` file extension. /// /// - parameter bundle: The bundle to search for all `.cer` files. /// /// - returns: All certificates within the given bundle. public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] { var certificates: [SecCertificate] = [] let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil) }.joined()) for path in paths { if let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, let certificate = SecCertificateCreateWithData(nil, certificateData) { certificates.append(certificate) } } return certificates } /// Returns all public keys within the given bundle with a `.cer` file extension. /// /// - parameter bundle: The bundle to search for all `*.cer` files. /// /// - returns: All public keys within the given bundle. public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] { var publicKeys: [SecKey] = [] for certificate in certificates(in: bundle) { if let publicKey = publicKey(for: certificate) { publicKeys.append(publicKey) } } return publicKeys } // MARK: - Evaluation /// Evaluates whether the server trust is valid for the given host. /// /// - parameter serverTrust: The server trust to evaluate. /// - parameter host: The host of the challenge protection space. /// /// - returns: Whether the server trust is valid. public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool { var serverTrustIsValid = false switch self { case let .performDefaultEvaluation(validateHost): let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) SecTrustSetPolicies(serverTrust, policy) serverTrustIsValid = trustIsValid(serverTrust) case let .performRevokedEvaluation(validateHost, revocationFlags): let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) let revokedPolicy = SecPolicyCreateRevocation(revocationFlags) SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef) serverTrustIsValid = trustIsValid(serverTrust) case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost): if validateCertificateChain { let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) SecTrustSetPolicies(serverTrust, policy) SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray) SecTrustSetAnchorCertificatesOnly(serverTrust, true) serverTrustIsValid = trustIsValid(serverTrust) } else { let serverCertificatesDataArray = certificateData(for: serverTrust) let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates) outerLoop: for serverCertificateData in serverCertificatesDataArray { for pinnedCertificateData in pinnedCertificatesDataArray { if serverCertificateData == pinnedCertificateData { serverTrustIsValid = true break outerLoop } } } } case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): var certificateChainEvaluationPassed = true if validateCertificateChain { let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) SecTrustSetPolicies(serverTrust, policy) certificateChainEvaluationPassed = trustIsValid(serverTrust) } if certificateChainEvaluationPassed { outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] { for pinnedPublicKey in pinnedPublicKeys as [AnyObject] { if serverPublicKey.isEqual(pinnedPublicKey) { serverTrustIsValid = true break outerLoop } } } } case .disableEvaluation: serverTrustIsValid = true case let .customEvaluation(closure): serverTrustIsValid = closure(serverTrust, host) } return serverTrustIsValid } // MARK: - Private - Trust Validation private func trustIsValid(_ trust: SecTrust) -> Bool { var isValid = false var result = SecTrustResultType.invalid let status = SecTrustEvaluate(trust, &result) if status == errSecSuccess { let unspecified = SecTrustResultType.unspecified let proceed = SecTrustResultType.proceed isValid = result == unspecified || result == proceed } return isValid } // MARK: - Private - Certificate Data private func certificateData(for trust: SecTrust) -> [Data] { var certificates: [SecCertificate] = [] for index in 0.. [Data] { return certificates.map { SecCertificateCopyData($0) as Data } } // MARK: - Private - Public Key Extraction private static func publicKeys(for trust: SecTrust) -> [SecKey] { var publicKeys: [SecKey] = [] for index in 0.. SecKey? { var publicKey: SecKey? let policy = SecPolicyCreateBasicX509() var trust: SecTrust? let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) if let trust = trust, trustCreationStatus == errSecSuccess { publicKey = SecTrustCopyPublicKey(trust) } return publicKey } } ================================================ FILE: Pods/Alamofire/Source/SessionDelegate.swift ================================================ // // SessionDelegate.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// Responsible for handling all delegate callbacks for the underlying session. open class SessionDelegate: NSObject { // MARK: URLSessionDelegate Overrides /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`. open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)? /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`. open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`. open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`. open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)? // MARK: URLSessionTaskDelegate Overrides /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`. open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and /// requires the caller to call the `completionHandler`. open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`. open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and /// requires the caller to call the `completionHandler`. open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`. open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and /// requires the caller to call the `completionHandler`. open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`. open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`. open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)? // MARK: URLSessionDataDelegate Overrides /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`. open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and /// requires caller to call the `completionHandler`. open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)? /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`. open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`. open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`. open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and /// requires caller to call the `completionHandler`. open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)? // MARK: URLSessionDownloadDelegate Overrides /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`. open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)? /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`. open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`. open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? // MARK: URLSessionStreamDelegate Overrides #if !os(watchOS) /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`. @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? { get { return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void } set { _streamTaskReadClosed = newValue } } /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`. @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? { get { return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void } set { _streamTaskWriteClosed = newValue } } /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`. @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? { get { return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void } set { _streamTaskBetterRouteDiscovered = newValue } } /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`. @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? { get { return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void } set { _streamTaskDidBecomeInputStream = newValue } } var _streamTaskReadClosed: Any? var _streamTaskWriteClosed: Any? var _streamTaskBetterRouteDiscovered: Any? var _streamTaskDidBecomeInputStream: Any? #endif // MARK: Properties var retrier: RequestRetrier? weak var sessionManager: SessionManager? private var requests: [Int: Request] = [:] private let lock = NSLock() /// Access the task delegate for the specified task in a thread-safe manner. open subscript(task: URLSessionTask) -> Request? { get { lock.lock() ; defer { lock.unlock() } return requests[task.taskIdentifier] } set { lock.lock() ; defer { lock.unlock() } requests[task.taskIdentifier] = newValue } } // MARK: Lifecycle /// Initializes the `SessionDelegate` instance. /// /// - returns: The new `SessionDelegate` instance. public override init() { super.init() } // MARK: NSObject Overrides /// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond /// to a specified message. /// /// - parameter selector: A selector that identifies a message. /// /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`. open override func responds(to selector: Selector) -> Bool { #if !os(macOS) if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) { return sessionDidFinishEventsForBackgroundURLSession != nil } #endif #if !os(watchOS) if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) { switch selector { case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)): return streamTaskReadClosed != nil case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)): return streamTaskWriteClosed != nil case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)): return streamTaskBetterRouteDiscovered != nil case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)): return streamTaskDidBecomeInputAndOutputStreams != nil default: break } } #endif switch selector { case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)): return sessionDidBecomeInvalidWithError != nil case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)): return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil) case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)): return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil) case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)): return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil) default: return type(of: self).instancesRespond(to: selector) } } } // MARK: - URLSessionDelegate extension SessionDelegate: URLSessionDelegate { /// Tells the delegate that the session has been invalidated. /// /// - parameter session: The session object that was invalidated. /// - parameter error: The error that caused invalidation, or nil if the invalidation was explicit. open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { sessionDidBecomeInvalidWithError?(session, error) } /// Requests credentials from the delegate in response to a session-level authentication request from the /// remote server. /// /// - parameter session: The session containing the task that requested authentication. /// - parameter challenge: An object that contains the request for authentication. /// - parameter completionHandler: A handler that your delegate method must call providing the disposition /// and credential. open func urlSession( _ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard sessionDidReceiveChallengeWithCompletion == nil else { sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler) return } var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling var credential: URLCredential? if let sessionDidReceiveChallenge = sessionDidReceiveChallenge { (disposition, credential) = sessionDidReceiveChallenge(session, challenge) } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { let host = challenge.protectionSpace.host if let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), let serverTrust = challenge.protectionSpace.serverTrust { if serverTrustPolicy.evaluate(serverTrust, forHost: host) { disposition = .useCredential credential = URLCredential(trust: serverTrust) } else { disposition = .cancelAuthenticationChallenge } } } completionHandler(disposition, credential) } #if !os(macOS) /// Tells the delegate that all messages enqueued for a session have been delivered. /// /// - parameter session: The session that no longer has any outstanding requests. open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { sessionDidFinishEventsForBackgroundURLSession?(session) } #endif } // MARK: - URLSessionTaskDelegate extension SessionDelegate: URLSessionTaskDelegate { /// Tells the delegate that the remote server requested an HTTP redirect. /// /// - parameter session: The session containing the task whose request resulted in a redirect. /// - parameter task: The task whose request resulted in a redirect. /// - parameter response: An object containing the server’s response to the original request. /// - parameter request: A URL request object filled out with the new location. /// - parameter completionHandler: A closure that your handler should call with either the value of the request /// parameter, a modified URL request object, or NULL to refuse the redirect and /// return the body of the redirect response. open func urlSession( _ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { guard taskWillPerformHTTPRedirectionWithCompletion == nil else { taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler) return } var redirectRequest: URLRequest? = request if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) } completionHandler(redirectRequest) } /// Requests credentials from the delegate in response to an authentication request from the remote server. /// /// - parameter session: The session containing the task whose request requires authentication. /// - parameter task: The task whose request requires authentication. /// - parameter challenge: An object that contains the request for authentication. /// - parameter completionHandler: A handler that your delegate method must call providing the disposition /// and credential. open func urlSession( _ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard taskDidReceiveChallengeWithCompletion == nil else { taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler) return } if let taskDidReceiveChallenge = taskDidReceiveChallenge { let result = taskDidReceiveChallenge(session, task, challenge) completionHandler(result.0, result.1) } else if let delegate = self[task]?.delegate { delegate.urlSession( session, task: task, didReceive: challenge, completionHandler: completionHandler ) } else { urlSession(session, didReceive: challenge, completionHandler: completionHandler) } } /// Tells the delegate when a task requires a new request body stream to send to the remote server. /// /// - parameter session: The session containing the task that needs a new body stream. /// - parameter task: The task that needs a new body stream. /// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream. open func urlSession( _ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) { guard taskNeedNewBodyStreamWithCompletion == nil else { taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler) return } if let taskNeedNewBodyStream = taskNeedNewBodyStream { completionHandler(taskNeedNewBodyStream(session, task)) } else if let delegate = self[task]?.delegate { delegate.urlSession(session, task: task, needNewBodyStream: completionHandler) } } /// Periodically informs the delegate of the progress of sending body content to the server. /// /// - parameter session: The session containing the data task. /// - parameter task: The data task. /// - parameter bytesSent: The number of bytes sent since the last time this delegate method was called. /// - parameter totalBytesSent: The total number of bytes sent so far. /// - parameter totalBytesExpectedToSend: The expected length of the body data. open func urlSession( _ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { if let taskDidSendBodyData = taskDidSendBodyData { taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) } else if let delegate = self[task]?.delegate as? UploadTaskDelegate { delegate.URLSession( session, task: task, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend ) } } #if !os(watchOS) /// Tells the delegate that the session finished collecting metrics for the task. /// /// - parameter session: The session collecting the metrics. /// - parameter task: The task whose metrics have been collected. /// - parameter metrics: The collected metrics. @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) @objc(URLSession:task:didFinishCollectingMetrics:) open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { self[task]?.delegate.metrics = metrics } #endif /// Tells the delegate that the task finished transferring data. /// /// - parameter session: The session containing the task whose request finished transferring data. /// - parameter task: The task whose request finished transferring data. /// - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil. open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { /// Executed after it is determined that the request is not going to be retried let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in guard let strongSelf = self else { return } strongSelf.taskDidComplete?(session, task, error) strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error) NotificationCenter.default.post( name: Notification.Name.Task.DidComplete, object: strongSelf, userInfo: [Notification.Key.Task: task] ) strongSelf[task] = nil } guard let request = self[task], let sessionManager = sessionManager else { completeTask(session, task, error) return } // Run all validations on the request before checking if an error occurred request.validations.forEach { $0() } // Determine whether an error has occurred var error: Error? = error if request.delegate.error != nil { error = request.delegate.error } /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request /// should be retried. Otherwise, complete the task by notifying the task delegate. if let retrier = retrier, let error = error { retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in guard shouldRetry else { completeTask(session, task, error) ; return } DispatchQueue.utility.after(timeDelay) { [weak self] in guard let strongSelf = self else { return } let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false if retrySucceeded, let task = request.task { strongSelf[task] = request return } else { completeTask(session, task, error) } } } } else { completeTask(session, task, error) } } } // MARK: - URLSessionDataDelegate extension SessionDelegate: URLSessionDataDelegate { /// Tells the delegate that the data task received the initial reply (headers) from the server. /// /// - parameter session: The session containing the data task that received an initial reply. /// - parameter dataTask: The data task that received an initial reply. /// - parameter response: A URL response object populated with headers. /// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a /// constant to indicate whether the transfer should continue as a data task or /// should become a download task. open func urlSession( _ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { guard dataTaskDidReceiveResponseWithCompletion == nil else { dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler) return } var disposition: URLSession.ResponseDisposition = .allow if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { disposition = dataTaskDidReceiveResponse(session, dataTask, response) } completionHandler(disposition) } /// Tells the delegate that the data task was changed to a download task. /// /// - parameter session: The session containing the task that was replaced by a download task. /// - parameter dataTask: The data task that was replaced by a download task. /// - parameter downloadTask: The new download task that replaced the data task. open func urlSession( _ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask) { if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask { dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask) } else { self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask) } } /// Tells the delegate that the data task has received some of the expected data. /// /// - parameter session: The session containing the data task that provided data. /// - parameter dataTask: The data task that provided data. /// - parameter data: A data object containing the transferred data. open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { if let dataTaskDidReceiveData = dataTaskDidReceiveData { dataTaskDidReceiveData(session, dataTask, data) } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate { delegate.urlSession(session, dataTask: dataTask, didReceive: data) } } /// Asks the delegate whether the data (or upload) task should store the response in the cache. /// /// - parameter session: The session containing the data (or upload) task. /// - parameter dataTask: The data (or upload) task. /// - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current /// caching policy and the values of certain received headers, such as the Pragma /// and Cache-Control headers. /// - parameter completionHandler: A block that your handler must call, providing either the original proposed /// response, a modified version of that response, or NULL to prevent caching the /// response. If your delegate implements this method, it must call this completion /// handler; otherwise, your app leaks memory. open func urlSession( _ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) { guard dataTaskWillCacheResponseWithCompletion == nil else { dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler) return } if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse)) } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate { delegate.urlSession( session, dataTask: dataTask, willCacheResponse: proposedResponse, completionHandler: completionHandler ) } else { completionHandler(proposedResponse) } } } // MARK: - URLSessionDownloadDelegate extension SessionDelegate: URLSessionDownloadDelegate { /// Tells the delegate that a download task has finished downloading. /// /// - parameter session: The session containing the download task that finished. /// - parameter downloadTask: The download task that finished. /// - parameter location: A file URL for the temporary file. Because the file is temporary, you must either /// open the file for reading or move it to a permanent location in your app’s sandbox /// container directory before returning from this delegate method. open func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL { downloadTaskDidFinishDownloadingToURL(session, downloadTask, location) } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate { delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) } } /// Periodically informs the delegate about the download’s progress. /// /// - parameter session: The session containing the download task. /// - parameter downloadTask: The download task. /// - parameter bytesWritten: The number of bytes transferred since the last time this delegate /// method was called. /// - parameter totalBytesWritten: The total number of bytes transferred so far. /// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length /// header. If this header was not provided, the value is /// `NSURLSessionTransferSizeUnknown`. open func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { if let downloadTaskDidWriteData = downloadTaskDidWriteData { downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate { delegate.urlSession( session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite ) } } /// Tells the delegate that the download task has resumed downloading. /// /// - parameter session: The session containing the download task that finished. /// - parameter downloadTask: The download task that resumed. See explanation in the discussion. /// - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the /// existing content, then this value is zero. Otherwise, this value is an /// integer representing the number of bytes on disk that do not need to be /// retrieved again. /// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header. /// If this header was not provided, the value is NSURLSessionTransferSizeUnknown. open func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) { if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate { delegate.urlSession( session, downloadTask: downloadTask, didResumeAtOffset: fileOffset, expectedTotalBytes: expectedTotalBytes ) } } } // MARK: - URLSessionStreamDelegate #if !os(watchOS) @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) extension SessionDelegate: URLSessionStreamDelegate { /// Tells the delegate that the read side of the connection has been closed. /// /// - parameter session: The session. /// - parameter streamTask: The stream task. open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) { streamTaskReadClosed?(session, streamTask) } /// Tells the delegate that the write side of the connection has been closed. /// /// - parameter session: The session. /// - parameter streamTask: The stream task. open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) { streamTaskWriteClosed?(session, streamTask) } /// Tells the delegate that the system has determined that a better route to the host is available. /// /// - parameter session: The session. /// - parameter streamTask: The stream task. open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) { streamTaskBetterRouteDiscovered?(session, streamTask) } /// Tells the delegate that the stream task has been completed and provides the unopened stream objects. /// /// - parameter session: The session. /// - parameter streamTask: The stream task. /// - parameter inputStream: The new input stream. /// - parameter outputStream: The new output stream. open func urlSession( _ session: URLSession, streamTask: URLSessionStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream) { streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream) } } #endif ================================================ FILE: Pods/Alamofire/Source/SessionManager.swift ================================================ // // SessionManager.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`. open class SessionManager { // MARK: - Helper Types /// Defines whether the `MultipartFormData` encoding was successful and contains result of the encoding as /// associated values. /// /// - Success: Represents a successful `MultipartFormData` encoding and contains the new `UploadRequest` along with /// streaming information. /// - Failure: Used to represent a failure in the `MultipartFormData` encoding and also contains the encoding /// error. public enum MultipartFormDataEncodingResult { case success(request: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?) case failure(Error) } // MARK: - Properties /// A default instance of `SessionManager`, used by top-level Alamofire request methods, and suitable for use /// directly for any ad hoc requests. open static let `default`: SessionManager = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders return SessionManager(configuration: configuration) }() /// Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers. open static let defaultHTTPHeaders: HTTPHeaders = { // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3 let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5" // Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5 let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in let quality = 1.0 - (Double(index) * 0.1) return "\(languageCode);q=\(quality)" }.joined(separator: ", ") // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3 // Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0` let userAgent: String = { if let info = Bundle.main.infoDictionary { let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown" let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown" let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown" let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown" let osNameVersion: String = { let version = ProcessInfo.processInfo.operatingSystemVersion let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)" let osName: String = { #if os(iOS) return "iOS" #elseif os(watchOS) return "watchOS" #elseif os(tvOS) return "tvOS" #elseif os(macOS) return "OS X" #elseif os(Linux) return "Linux" #else return "Unknown" #endif }() return "\(osName) \(versionString)" }() let alamofireVersion: String = { guard let afInfo = Bundle(for: SessionManager.self).infoDictionary, let build = afInfo["CFBundleShortVersionString"] else { return "Unknown" } return "Alamofire/\(build)" }() return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)" } return "Alamofire" }() return [ "Accept-Encoding": acceptEncoding, "Accept-Language": acceptLanguage, "User-Agent": userAgent ] }() /// Default memory threshold used when encoding `MultipartFormData` in bytes. open static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000 /// The underlying session. open let session: URLSession /// The session delegate handling all the task and session delegate callbacks. open let delegate: SessionDelegate /// Whether to start requests immediately after being constructed. `true` by default. open var startRequestsImmediately: Bool = true /// The request adapter called each time a new request is created. open var adapter: RequestAdapter? /// The request retrier called each time a request encounters an error to determine whether to retry the request. open var retrier: RequestRetrier? { get { return delegate.retrier } set { delegate.retrier = newValue } } /// The background completion handler closure provided by the UIApplicationDelegate /// `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background /// completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation /// will automatically call the handler. /// /// If you need to handle your own events before the handler is called, then you need to override the /// SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished. /// /// `nil` by default. open var backgroundCompletionHandler: (() -> Void)? let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString) // MARK: - Lifecycle /// Creates an instance with the specified `configuration`, `delegate` and `serverTrustPolicyManager`. /// /// - parameter configuration: The configuration used to construct the managed session. /// `URLSessionConfiguration.default` by default. /// - parameter delegate: The delegate used when initializing the session. `SessionDelegate()` by /// default. /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust /// challenges. `nil` by default. /// /// - returns: The new `SessionManager` instance. public init( configuration: URLSessionConfiguration = URLSessionConfiguration.default, delegate: SessionDelegate = SessionDelegate(), serverTrustPolicyManager: ServerTrustPolicyManager? = nil) { self.delegate = delegate self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) commonInit(serverTrustPolicyManager: serverTrustPolicyManager) } /// Creates an instance with the specified `session`, `delegate` and `serverTrustPolicyManager`. /// /// - parameter session: The URL session. /// - parameter delegate: The delegate of the URL session. Must equal the URL session's delegate. /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust /// challenges. `nil` by default. /// /// - returns: The new `SessionManager` instance if the URL session's delegate matches; `nil` otherwise. public init?( session: URLSession, delegate: SessionDelegate, serverTrustPolicyManager: ServerTrustPolicyManager? = nil) { guard delegate === session.delegate else { return nil } self.delegate = delegate self.session = session commonInit(serverTrustPolicyManager: serverTrustPolicyManager) } private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) { session.serverTrustPolicyManager = serverTrustPolicyManager delegate.sessionManager = self delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in guard let strongSelf = self else { return } DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() } } } deinit { session.invalidateAndCancel() } // MARK: - Data Request /// Creates a `DataRequest` to retrieve the contents of the specified `url`, `method`, `parameters`, `encoding` /// and `headers`. /// /// - parameter url: The URL. /// - parameter method: The HTTP method. `.get` by default. /// - parameter parameters: The parameters. `nil` by default. /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `DataRequest`. @discardableResult open func request( _ url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) -> DataRequest { var originalRequest: URLRequest? do { originalRequest = try URLRequest(url: url, method: method, headers: headers) let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters) return request(encodedURLRequest) } catch { return request(originalRequest, failedWith: error) } } /// Creates a `DataRequest` to retrieve the contents of a URL based on the specified `urlRequest`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter urlRequest: The URL request. /// /// - returns: The created `DataRequest`. open func request(_ urlRequest: URLRequestConvertible) -> DataRequest { var originalRequest: URLRequest? do { originalRequest = try urlRequest.asURLRequest() let originalTask = DataRequest.Requestable(urlRequest: originalRequest!) let task = try originalTask.task(session: session, adapter: adapter, queue: queue) let request = DataRequest(session: session, requestTask: .data(originalTask, task)) delegate[task] = request if startRequestsImmediately { request.resume() } return request } catch { return request(originalRequest, failedWith: error) } } // MARK: Private - Request Implementation private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest { var requestTask: Request.RequestTask = .data(nil, nil) if let urlRequest = urlRequest { let originalTask = DataRequest.Requestable(urlRequest: urlRequest) requestTask = .data(originalTask, nil) } let underlyingError = error.underlyingAdaptError ?? error let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError) if let retrier = retrier, error is AdaptError { allowRetrier(retrier, toRetry: request, with: underlyingError) } else { if startRequestsImmediately { request.resume() } } return request } // MARK: - Download Request // MARK: URL Request /// Creates a `DownloadRequest` to retrieve the contents the specified `url`, `method`, `parameters`, `encoding`, /// `headers` and save them to the `destination`. /// /// If `destination` is not specified, the contents will remain in the temporary location determined by the /// underlying URL session. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter url: The URL. /// - parameter method: The HTTP method. `.get` by default. /// - parameter parameters: The parameters. `nil` by default. /// - parameter encoding: The parameter encoding. `URLEncoding.default` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. /// /// - returns: The created `DownloadRequest`. @discardableResult open func download( _ url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil, to destination: DownloadRequest.DownloadFileDestination? = nil) -> DownloadRequest { do { let urlRequest = try URLRequest(url: url, method: method, headers: headers) let encodedURLRequest = try encoding.encode(urlRequest, with: parameters) return download(encodedURLRequest, to: destination) } catch { return download(nil, to: destination, failedWith: error) } } /// Creates a `DownloadRequest` to retrieve the contents of a URL based on the specified `urlRequest` and save /// them to the `destination`. /// /// If `destination` is not specified, the contents will remain in the temporary location determined by the /// underlying URL session. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter urlRequest: The URL request /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. /// /// - returns: The created `DownloadRequest`. @discardableResult open func download( _ urlRequest: URLRequestConvertible, to destination: DownloadRequest.DownloadFileDestination? = nil) -> DownloadRequest { do { let urlRequest = try urlRequest.asURLRequest() return download(.request(urlRequest), to: destination) } catch { return download(nil, to: destination, failedWith: error) } } // MARK: Resume Data /// Creates a `DownloadRequest` from the `resumeData` produced from a previous request cancellation to retrieve /// the contents of the original request and save them to the `destination`. /// /// If `destination` is not specified, the contents will remain in the temporary location determined by the /// underlying URL session. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken /// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the /// data is written incorrectly and will always fail to resume the download. For more information about the bug and /// possible workarounds, please refer to the following Stack Overflow post: /// /// - http://stackoverflow.com/a/39347461/1342462 /// /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask` /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for /// additional information. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default. /// /// - returns: The created `DownloadRequest`. @discardableResult open func download( resumingWith resumeData: Data, to destination: DownloadRequest.DownloadFileDestination? = nil) -> DownloadRequest { return download(.resumeData(resumeData), to: destination) } // MARK: Private - Download Implementation private func download( _ downloadable: DownloadRequest.Downloadable, to destination: DownloadRequest.DownloadFileDestination?) -> DownloadRequest { do { let task = try downloadable.task(session: session, adapter: adapter, queue: queue) let download = DownloadRequest(session: session, requestTask: .download(downloadable, task)) download.downloadDelegate.destination = destination delegate[task] = download if startRequestsImmediately { download.resume() } return download } catch { return download(downloadable, to: destination, failedWith: error) } } private func download( _ downloadable: DownloadRequest.Downloadable?, to destination: DownloadRequest.DownloadFileDestination?, failedWith error: Error) -> DownloadRequest { var downloadTask: Request.RequestTask = .download(nil, nil) if let downloadable = downloadable { downloadTask = .download(downloadable, nil) } let underlyingError = error.underlyingAdaptError ?? error let download = DownloadRequest(session: session, requestTask: downloadTask, error: underlyingError) download.downloadDelegate.destination = destination if let retrier = retrier, error is AdaptError { allowRetrier(retrier, toRetry: download, with: underlyingError) } else { if startRequestsImmediately { download.resume() } } return download } // MARK: - Upload Request // MARK: File /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `file`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter file: The file to upload. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `UploadRequest`. @discardableResult open func upload( _ fileURL: URL, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil) -> UploadRequest { do { let urlRequest = try URLRequest(url: url, method: method, headers: headers) return upload(fileURL, with: urlRequest) } catch { return upload(nil, failedWith: error) } } /// Creates a `UploadRequest` from the specified `urlRequest` for uploading the `file`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter file: The file to upload. /// - parameter urlRequest: The URL request. /// /// - returns: The created `UploadRequest`. @discardableResult open func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest { do { let urlRequest = try urlRequest.asURLRequest() return upload(.file(fileURL, urlRequest)) } catch { return upload(nil, failedWith: error) } } // MARK: Data /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `data`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter data: The data to upload. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `UploadRequest`. @discardableResult open func upload( _ data: Data, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil) -> UploadRequest { do { let urlRequest = try URLRequest(url: url, method: method, headers: headers) return upload(data, with: urlRequest) } catch { return upload(nil, failedWith: error) } } /// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `data`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter data: The data to upload. /// - parameter urlRequest: The URL request. /// /// - returns: The created `UploadRequest`. @discardableResult open func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest { do { let urlRequest = try urlRequest.asURLRequest() return upload(.data(data, urlRequest)) } catch { return upload(nil, failedWith: error) } } // MARK: InputStream /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `stream`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter stream: The stream to upload. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// /// - returns: The created `UploadRequest`. @discardableResult open func upload( _ stream: InputStream, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil) -> UploadRequest { do { let urlRequest = try URLRequest(url: url, method: method, headers: headers) return upload(stream, with: urlRequest) } catch { return upload(nil, failedWith: error) } } /// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `stream`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter stream: The stream to upload. /// - parameter urlRequest: The URL request. /// /// - returns: The created `UploadRequest`. @discardableResult open func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest { do { let urlRequest = try urlRequest.asURLRequest() return upload(.stream(stream, urlRequest)) } catch { return upload(nil, failedWith: error) } } // MARK: MultipartFormData /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new /// `UploadRequest` using the `url`, `method` and `headers`. /// /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be /// used for larger payloads such as video content. /// /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding /// technique was used. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. /// `multipartFormDataEncodingMemoryThreshold` by default. /// - parameter url: The URL. /// - parameter method: The HTTP method. `.post` by default. /// - parameter headers: The HTTP headers. `nil` by default. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. open func upload( multipartFormData: @escaping (MultipartFormData) -> Void, usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, to url: URLConvertible, method: HTTPMethod = .post, headers: HTTPHeaders? = nil, encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) { do { let urlRequest = try URLRequest(url: url, method: method, headers: headers) return upload( multipartFormData: multipartFormData, usingThreshold: encodingMemoryThreshold, with: urlRequest, encodingCompletion: encodingCompletion ) } catch { DispatchQueue.main.async { encodingCompletion?(.failure(error)) } } } /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new /// `UploadRequest` using the `urlRequest`. /// /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be /// used for larger payloads such as video content. /// /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding /// technique was used. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. /// `multipartFormDataEncodingMemoryThreshold` by default. /// - parameter urlRequest: The URL request. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. open func upload( multipartFormData: @escaping (MultipartFormData) -> Void, usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold, with urlRequest: URLRequestConvertible, encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) { DispatchQueue.global(qos: .utility).async { let formData = MultipartFormData() multipartFormData(formData) var tempFileURL: URL? do { var urlRequestWithContentType = try urlRequest.asURLRequest() urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type") let isBackgroundSession = self.session.configuration.identifier != nil if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession { let data = try formData.encode() let encodingResult = MultipartFormDataEncodingResult.success( request: self.upload(data, with: urlRequestWithContentType), streamingFromDisk: false, streamFileURL: nil ) DispatchQueue.main.async { encodingCompletion?(encodingResult) } } else { let fileManager = FileManager.default let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()) let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data") let fileName = UUID().uuidString let fileURL = directoryURL.appendingPathComponent(fileName) tempFileURL = fileURL var directoryError: Error? // Create directory inside serial queue to ensure two threads don't do this in parallel self.queue.sync { do { try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) } catch { directoryError = error } } if let directoryError = directoryError { throw directoryError } try formData.writeEncodedData(to: fileURL) let upload = self.upload(fileURL, with: urlRequestWithContentType) // Cleanup the temp file once the upload is complete upload.delegate.queue.addOperation { do { try FileManager.default.removeItem(at: fileURL) } catch { // No-op } } DispatchQueue.main.async { let encodingResult = MultipartFormDataEncodingResult.success( request: upload, streamingFromDisk: true, streamFileURL: fileURL ) encodingCompletion?(encodingResult) } } } catch { // Cleanup the temp file in the event that the multipart form data encoding failed if let tempFileURL = tempFileURL { do { try FileManager.default.removeItem(at: tempFileURL) } catch { // No-op } } DispatchQueue.main.async { encodingCompletion?(.failure(error)) } } } } // MARK: Private - Upload Implementation private func upload(_ uploadable: UploadRequest.Uploadable) -> UploadRequest { do { let task = try uploadable.task(session: session, adapter: adapter, queue: queue) let upload = UploadRequest(session: session, requestTask: .upload(uploadable, task)) if case let .stream(inputStream, _) = uploadable { upload.delegate.taskNeedNewBodyStream = { _, _ in inputStream } } delegate[task] = upload if startRequestsImmediately { upload.resume() } return upload } catch { return upload(uploadable, failedWith: error) } } private func upload(_ uploadable: UploadRequest.Uploadable?, failedWith error: Error) -> UploadRequest { var uploadTask: Request.RequestTask = .upload(nil, nil) if let uploadable = uploadable { uploadTask = .upload(uploadable, nil) } let underlyingError = error.underlyingAdaptError ?? error let upload = UploadRequest(session: session, requestTask: uploadTask, error: underlyingError) if let retrier = retrier, error is AdaptError { allowRetrier(retrier, toRetry: upload, with: underlyingError) } else { if startRequestsImmediately { upload.resume() } } return upload } #if !os(watchOS) // MARK: - Stream Request // MARK: Hostname and Port /// Creates a `StreamRequest` for bidirectional streaming using the `hostname` and `port`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter hostName: The hostname of the server to connect to. /// - parameter port: The port of the server to connect to. /// /// - returns: The created `StreamRequest`. @discardableResult @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) open func stream(withHostName hostName: String, port: Int) -> StreamRequest { return stream(.stream(hostName: hostName, port: port)) } // MARK: NetService /// Creates a `StreamRequest` for bidirectional streaming using the `netService`. /// /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. /// /// - parameter netService: The net service used to identify the endpoint. /// /// - returns: The created `StreamRequest`. @discardableResult @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) open func stream(with netService: NetService) -> StreamRequest { return stream(.netService(netService)) } // MARK: Private - Stream Implementation @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) private func stream(_ streamable: StreamRequest.Streamable) -> StreamRequest { do { let task = try streamable.task(session: session, adapter: adapter, queue: queue) let request = StreamRequest(session: session, requestTask: .stream(streamable, task)) delegate[task] = request if startRequestsImmediately { request.resume() } return request } catch { return stream(failedWith: error) } } @available(iOS 9.0, macOS 10.11, tvOS 9.0, *) private func stream(failedWith error: Error) -> StreamRequest { let stream = StreamRequest(session: session, requestTask: .stream(nil, nil), error: error) if startRequestsImmediately { stream.resume() } return stream } #endif // MARK: - Internal - Retry Request func retry(_ request: Request) -> Bool { guard let originalTask = request.originalTask else { return false } do { let task = try originalTask.task(session: session, adapter: adapter, queue: queue) request.delegate.task = task // resets all task delegate data request.retryCount += 1 request.startTime = CFAbsoluteTimeGetCurrent() request.endTime = nil task.resume() return true } catch { request.delegate.error = error.underlyingAdaptError ?? error return false } } private func allowRetrier(_ retrier: RequestRetrier, toRetry request: Request, with error: Error) { DispatchQueue.utility.async { [weak self] in guard let strongSelf = self else { return } retrier.should(strongSelf, retry: request, with: error) { shouldRetry, timeDelay in guard let strongSelf = self else { return } guard shouldRetry else { if strongSelf.startRequestsImmediately { request.resume() } return } DispatchQueue.utility.after(timeDelay) { guard let strongSelf = self else { return } let retrySucceeded = strongSelf.retry(request) if retrySucceeded, let task = request.task { strongSelf.delegate[task] = request } else { if strongSelf.startRequestsImmediately { request.resume() } } } } } } } ================================================ FILE: Pods/Alamofire/Source/TaskDelegate.swift ================================================ // // TaskDelegate.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as /// executing all operations attached to the serial operation queue upon task completion. open class TaskDelegate: NSObject { // MARK: Properties /// The serial operation queue used to execute all operations after the task completes. open let queue: OperationQueue /// The data returned by the server. public var data: Data? { return nil } /// The error generated throughout the lifecyle of the task. public var error: Error? var task: URLSessionTask? { set { taskLock.lock(); defer { taskLock.unlock() } _task = newValue } get { taskLock.lock(); defer { taskLock.unlock() } return _task } } var initialResponseTime: CFAbsoluteTime? var credential: URLCredential? var metrics: AnyObject? // URLSessionTaskMetrics private var _task: URLSessionTask? { didSet { reset() } } private let taskLock = NSLock() // MARK: Lifecycle init(task: URLSessionTask?) { _task = task self.queue = { let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 operationQueue.isSuspended = true operationQueue.qualityOfService = .utility return operationQueue }() } func reset() { error = nil initialResponseTime = nil } // MARK: URLSessionTaskDelegate var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)? @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) func urlSession( _ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { var redirectRequest: URLRequest? = request if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) } completionHandler(redirectRequest) } @objc(URLSession:task:didReceiveChallenge:completionHandler:) func urlSession( _ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling var credential: URLCredential? if let taskDidReceiveChallenge = taskDidReceiveChallenge { (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { let host = challenge.protectionSpace.host if let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), let serverTrust = challenge.protectionSpace.serverTrust { if serverTrustPolicy.evaluate(serverTrust, forHost: host) { disposition = .useCredential credential = URLCredential(trust: serverTrust) } else { disposition = .cancelAuthenticationChallenge } } } else { if challenge.previousFailureCount > 0 { disposition = .rejectProtectionSpace } else { credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace) if credential != nil { disposition = .useCredential } } } completionHandler(disposition, credential) } @objc(URLSession:task:needNewBodyStream:) func urlSession( _ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) { var bodyStream: InputStream? if let taskNeedNewBodyStream = taskNeedNewBodyStream { bodyStream = taskNeedNewBodyStream(session, task) } completionHandler(bodyStream) } @objc(URLSession:task:didCompleteWithError:) func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let taskDidCompleteWithError = taskDidCompleteWithError { taskDidCompleteWithError(session, task, error) } else { if let error = error { if self.error == nil { self.error = error } if let downloadDelegate = self as? DownloadTaskDelegate, let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data { downloadDelegate.resumeData = resumeData } } queue.isSuspended = false } } } // MARK: - class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate { // MARK: Properties var dataTask: URLSessionDataTask { return task as! URLSessionDataTask } override var data: Data? { if dataStream != nil { return nil } else { return mutableData } } var progress: Progress var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? var dataStream: ((_ data: Data) -> Void)? private var totalBytesReceived: Int64 = 0 private var mutableData: Data private var expectedContentLength: Int64? // MARK: Lifecycle override init(task: URLSessionTask?) { mutableData = Data() progress = Progress(totalUnitCount: 0) super.init(task: task) } override func reset() { super.reset() progress = Progress(totalUnitCount: 0) totalBytesReceived = 0 mutableData = Data() expectedContentLength = nil } // MARK: URLSessionDataDelegate var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? func urlSession( _ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { var disposition: URLSession.ResponseDisposition = .allow expectedContentLength = response.expectedContentLength if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { disposition = dataTaskDidReceiveResponse(session, dataTask, response) } completionHandler(disposition) } func urlSession( _ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask) { dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } if let dataTaskDidReceiveData = dataTaskDidReceiveData { dataTaskDidReceiveData(session, dataTask, data) } else { if let dataStream = dataStream { dataStream(data) } else { mutableData.append(data) } let bytesReceived = Int64(data.count) totalBytesReceived += bytesReceived let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown progress.totalUnitCount = totalBytesExpected progress.completedUnitCount = totalBytesReceived if let progressHandler = progressHandler { progressHandler.queue.async { progressHandler.closure(self.progress) } } } } func urlSession( _ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) { var cachedResponse: CachedURLResponse? = proposedResponse if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) } completionHandler(cachedResponse) } } // MARK: - class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate { // MARK: Properties var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask } var progress: Progress var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? var resumeData: Data? override var data: Data? { return resumeData } var destination: DownloadRequest.DownloadFileDestination? var temporaryURL: URL? var destinationURL: URL? var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL } // MARK: Lifecycle override init(task: URLSessionTask?) { progress = Progress(totalUnitCount: 0) super.init(task: task) } override func reset() { super.reset() progress = Progress(totalUnitCount: 0) resumeData = nil } // MARK: URLSessionDownloadDelegate var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)? var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { temporaryURL = location guard let destination = destination, let response = downloadTask.response as? HTTPURLResponse else { return } let result = destination(location, response) let destinationURL = result.destinationURL let options = result.options self.destinationURL = destinationURL do { if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) { try FileManager.default.removeItem(at: destinationURL) } if options.contains(.createIntermediateDirectories) { let directory = destinationURL.deletingLastPathComponent() try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) } try FileManager.default.moveItem(at: location, to: destinationURL) } catch { self.error = error } } func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } if let downloadTaskDidWriteData = downloadTaskDidWriteData { downloadTaskDidWriteData( session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite ) } else { progress.totalUnitCount = totalBytesExpectedToWrite progress.completedUnitCount = totalBytesWritten if let progressHandler = progressHandler { progressHandler.queue.async { progressHandler.closure(self.progress) } } } } func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) { if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) } else { progress.totalUnitCount = expectedTotalBytes progress.completedUnitCount = fileOffset } } } // MARK: - class UploadTaskDelegate: DataTaskDelegate { // MARK: Properties var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask } var uploadProgress: Progress var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? // MARK: Lifecycle override init(task: URLSessionTask?) { uploadProgress = Progress(totalUnitCount: 0) super.init(task: task) } override func reset() { super.reset() uploadProgress = Progress(totalUnitCount: 0) } // MARK: URLSessionTaskDelegate var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? func URLSession( _ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } if let taskDidSendBodyData = taskDidSendBodyData { taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) } else { uploadProgress.totalUnitCount = totalBytesExpectedToSend uploadProgress.completedUnitCount = totalBytesSent if let uploadProgressHandler = uploadProgressHandler { uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) } } } } } ================================================ FILE: Pods/Alamofire/Source/Timeline.swift ================================================ // // Timeline.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation /// Responsible for computing the timing metrics for the complete lifecycle of a `Request`. public struct Timeline { /// The time the request was initialized. public let requestStartTime: CFAbsoluteTime /// The time the first bytes were received from or sent to the server. public let initialResponseTime: CFAbsoluteTime /// The time when the request was completed. public let requestCompletedTime: CFAbsoluteTime /// The time when the response serialization was completed. public let serializationCompletedTime: CFAbsoluteTime /// The time interval in seconds from the time the request started to the initial response from the server. public let latency: TimeInterval /// The time interval in seconds from the time the request started to the time the request completed. public let requestDuration: TimeInterval /// The time interval in seconds from the time the request completed to the time response serialization completed. public let serializationDuration: TimeInterval /// The time interval in seconds from the time the request started to the time response serialization completed. public let totalDuration: TimeInterval /// Creates a new `Timeline` instance with the specified request times. /// /// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`. /// - parameter initialResponseTime: The time the first bytes were received from or sent to the server. /// Defaults to `0.0`. /// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`. /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults /// to `0.0`. /// /// - returns: The new `Timeline` instance. public init( requestStartTime: CFAbsoluteTime = 0.0, initialResponseTime: CFAbsoluteTime = 0.0, requestCompletedTime: CFAbsoluteTime = 0.0, serializationCompletedTime: CFAbsoluteTime = 0.0) { self.requestStartTime = requestStartTime self.initialResponseTime = initialResponseTime self.requestCompletedTime = requestCompletedTime self.serializationCompletedTime = serializationCompletedTime self.latency = initialResponseTime - requestStartTime self.requestDuration = requestCompletedTime - requestStartTime self.serializationDuration = serializationCompletedTime - requestCompletedTime self.totalDuration = serializationCompletedTime - requestStartTime } } // MARK: - CustomStringConvertible extension Timeline: CustomStringConvertible { /// The textual representation used when written to an output stream, which includes the latency, the request /// duration and the total duration. public var description: String { let latency = String(format: "%.3f", self.latency) let requestDuration = String(format: "%.3f", self.requestDuration) let serializationDuration = String(format: "%.3f", self.serializationDuration) let totalDuration = String(format: "%.3f", self.totalDuration) // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. let timings = [ "\"Latency\": " + latency + " secs", "\"Request Duration\": " + requestDuration + " secs", "\"Serialization Duration\": " + serializationDuration + " secs", "\"Total Duration\": " + totalDuration + " secs" ] return "Timeline: { " + timings.joined(separator: ", ") + " }" } } // MARK: - CustomDebugStringConvertible extension Timeline: CustomDebugStringConvertible { /// The textual representation used when written to an output stream, which includes the request start time, the /// initial response time, the request completed time, the serialization completed time, the latency, the request /// duration and the total duration. public var debugDescription: String { let requestStartTime = String(format: "%.3f", self.requestStartTime) let initialResponseTime = String(format: "%.3f", self.initialResponseTime) let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime) let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime) let latency = String(format: "%.3f", self.latency) let requestDuration = String(format: "%.3f", self.requestDuration) let serializationDuration = String(format: "%.3f", self.serializationDuration) let totalDuration = String(format: "%.3f", self.totalDuration) // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. let timings = [ "\"Request Start Time\": " + requestStartTime, "\"Initial Response Time\": " + initialResponseTime, "\"Request Completed Time\": " + requestCompletedTime, "\"Serialization Completed Time\": " + serializationCompletedTime, "\"Latency\": " + latency + " secs", "\"Request Duration\": " + requestDuration + " secs", "\"Serialization Duration\": " + serializationDuration + " secs", "\"Total Duration\": " + totalDuration + " secs" ] return "Timeline: { " + timings.joined(separator: ", ") + " }" } } ================================================ FILE: Pods/Alamofire/Source/Validation.swift ================================================ // // Validation.swift // // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) // // 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. // import Foundation extension Request { // MARK: Helper Types fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason /// Used to represent whether validation was successful or encountered an error resulting in a failure. /// /// - success: The validation was successful. /// - failure: The validation failed encountering the provided error. public enum ValidationResult { case success case failure(Error) } fileprivate struct MIMEType { let type: String let subtype: String var isWildcard: Bool { return type == "*" && subtype == "*" } init?(_ string: String) { let components: [String] = { let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) #if swift(>=3.2) let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] #else let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex) #endif return split.components(separatedBy: "/") }() if let type = components.first, let subtype = components.last { self.type = type self.subtype = subtype } else { return nil } } func matches(_ mime: MIMEType) -> Bool { switch (type, subtype) { case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): return true default: return false } } } // MARK: Properties fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) } fileprivate var acceptableContentTypes: [String] { if let accept = request?.value(forHTTPHeaderField: "Accept") { return accept.components(separatedBy: ",") } return ["*/*"] } // MARK: Status Code fileprivate func validate( statusCode acceptableStatusCodes: S, response: HTTPURLResponse) -> ValidationResult where S.Iterator.Element == Int { if acceptableStatusCodes.contains(response.statusCode) { return .success } else { let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) return .failure(AFError.responseValidationFailed(reason: reason)) } } // MARK: Content Type fileprivate func validate( contentType acceptableContentTypes: S, response: HTTPURLResponse, data: Data?) -> ValidationResult where S.Iterator.Element == String { guard let data = data, data.count > 0 else { return .success } guard let responseContentType = response.mimeType, let responseMIMEType = MIMEType(responseContentType) else { for contentType in acceptableContentTypes { if let mimeType = MIMEType(contentType), mimeType.isWildcard { return .success } } let error: AFError = { let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes)) return AFError.responseValidationFailed(reason: reason) }() return .failure(error) } for contentType in acceptableContentTypes { if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { return .success } } let error: AFError = { let reason: ErrorReason = .unacceptableContentType( acceptableContentTypes: Array(acceptableContentTypes), responseContentType: responseContentType ) return AFError.responseValidationFailed(reason: reason) }() return .failure(error) } } // MARK: - extension DataRequest { /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the /// request was valid. public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult /// Validates the request, using the specified closure. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - parameter validation: A closure to validate the request. /// /// - returns: The request. @discardableResult public func validate(_ validation: @escaping Validation) -> Self { let validationExecution: () -> Void = { [unowned self] in if let response = self.response, self.delegate.error == nil, case let .failure(error) = validation(self.request, response, self.delegate.data) { self.delegate.error = error } } validations.append(validationExecution) return self } /// Validates that the response has a status code in the specified sequence. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - parameter range: The range of acceptable status codes. /// /// - returns: The request. @discardableResult public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { return validate { [unowned self] _, response, _ in return self.validate(statusCode: acceptableStatusCodes, response: response) } } /// Validates that the response has a content type in the specified sequence. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. /// /// - returns: The request. @discardableResult public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { return validate { [unowned self] _, response, data in return self.validate(contentType: acceptableContentTypes, response: response, data: data) } } /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content /// type matches any specified in the Accept HTTP header field. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - returns: The request. @discardableResult public func validate() -> Self { return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) } } // MARK: - extension DownloadRequest { /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a /// destination URL, and returns whether the request was valid. public typealias Validation = ( _ request: URLRequest?, _ response: HTTPURLResponse, _ temporaryURL: URL?, _ destinationURL: URL?) -> ValidationResult /// Validates the request, using the specified closure. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - parameter validation: A closure to validate the request. /// /// - returns: The request. @discardableResult public func validate(_ validation: @escaping Validation) -> Self { let validationExecution: () -> Void = { [unowned self] in let request = self.request let temporaryURL = self.downloadDelegate.temporaryURL let destinationURL = self.downloadDelegate.destinationURL if let response = self.response, self.delegate.error == nil, case let .failure(error) = validation(request, response, temporaryURL, destinationURL) { self.delegate.error = error } } validations.append(validationExecution) return self } /// Validates that the response has a status code in the specified sequence. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - parameter range: The range of acceptable status codes. /// /// - returns: The request. @discardableResult public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { return validate { [unowned self] _, response, _, _ in return self.validate(statusCode: acceptableStatusCodes, response: response) } } /// Validates that the response has a content type in the specified sequence. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. /// /// - returns: The request. @discardableResult public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { return validate { [unowned self] _, response, _, _ in let fileURL = self.downloadDelegate.fileURL guard let validFileURL = fileURL else { return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) } do { let data = try Data(contentsOf: validFileURL) return self.validate(contentType: acceptableContentTypes, response: response, data: data) } catch { return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) } } } /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content /// type matches any specified in the Accept HTTP header field. /// /// If validation fails, subsequent calls to response handlers will have an associated error. /// /// - returns: The request. @discardableResult public func validate() -> Self { return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) } } ================================================ FILE: Pods/Differentiator/LICENSE.md ================================================ MIT License Copyright (c) 2017 RxSwift Community 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. ================================================ FILE: Pods/Differentiator/README.md ================================================ [![Travis CI](https://travis-ci.org/RxSwiftCommunity/RxDataSources.svg?branch=master)](https://travis-ci.org/RxSwiftCommunity/RxDataSources) Table and Collection view data sources ====================================== ## Features - [x] **O(N)** algorithm for calculating differences - the algorithm has the assumption that all sections and items are unique so there is no ambiguity - in case there is ambiguity, fallbacks automagically on non animated refresh - [x] it applies additional heuristics to send the least number of commands to sectioned view - even though the running time is linear, preferred number of sent commands is usually a lot less than linear - it is preferred (and possible) to cap the number of changes to some small number, and in case the number of changes grows towards linear, just do normal reload - [x] Supports **extending your item and section structures** - just extend your item with `IdentifiableType` and `Equatable`, and your section with `AnimatableSectionModelType` - [x] Supports all combinations of two level hierarchical animations for **both sections and items** - Section animations: Insert, Delete, Move - Item animations: Insert, Delete, Move, Reload (if old value is not equal to new value) - [x] Configurable animation types for `Insert`, `Reload` and `Delete` (Automatic, Fade, ...) - [x] Example app - [x] Randomized stress tests (example app) - [x] Supports editing out of the box (example app) - [x] Works with `UITableView` and `UICollectionView` ## Why Writing table and collection view data sources is tedious. There is a large number of delegate methods that need to be implemented for the simplest case possible. RxSwift helps alleviate some of the burden with a simple data binding mechanism: 1) Turn your data into an Observable sequence 2) Bind the data to the tableView/collectionView using one of: - `rx.items(dataSource:protocol)` - `rx.items(cellIdentifier:String)` - `rx.items(cellIdentifier:String:Cell.Type:_:)` - `rx.items(_:_:)` ```swift let data = Observable<[String]>.just(["first element", "second element", "third element"]) data.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in cell.textLabel?.text = model } .disposed(by: disposeBag) ``` This works well with simple data sets but does not handle cases where you need to bind complex data sets with multiples sections, or when you need to perform animations when adding/modifying/deleting items. These are precisely the use cases that RxDataSources helps solve. With RxDataSources, it is super easy to just write ```swift let dataSource = RxTableViewSectionedReloadDataSource>() Observable.just([SectionModel(model: "title", items: [1, 2, 3])]) .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) ``` ![RxDataSources example app](https://raw.githubusercontent.com/kzaher/rxswiftcontent/rxdatasources/RxDataSources.gif) ## How Given the following custom data structure: ```swift struct CustomData { var anInt: Int var aString: String var aCGPoint: CGPoint } ``` 1) Start by defining your sections with a struct that conforms to the `SectionModelType` protocol: - define the `Item` typealias: equal to the type of items that the section will contain - declare an `items` property: of type array of `Item` ```swift struct SectionOfCustomData { var header: String var items: [Item] } extension SectionOfCustomData: SectionModelType { typealias Item = CustomData init(original: SectionOfCustomData, items: [Item]) { self = original self.items = items } } ``` 2) Create a dataSource object and pass it your `SectionOfCustomData` type: ```swift let dataSource = RxTableViewSectionedReloadDataSource() ``` 3) Customize closures on the dataSource as needed: - `configureCell` (required) - `titleForHeaderInSection` - `titleForFooterInSection` - etc ```swift dataSource.configureCell = { (ds: RxTableViewSectionedReloadDataSource, tv: UITableView, ip: IndexPath, item: Item) in let cell = tv.dequeueReusableCell(withIdentifier: "Cell", for: ip) cell.textLabel?.text = "Item \(item.anInt): \(item.aString) - \(item.aCGPoint.x):\(item.aCGPoint.y)" return cell } dataSource.titleForHeaderInSection = { ds, index in return ds.sectionModels[index].header } ``` 4) Define the actual data as an Observable sequence of CustomData objects and bind it to the tableView ```swift let sections = [ SectionOfCustomData(header: "First section", items: [CustomData(anInt: 0, aString: "zero", aCGPoint: CGPoint.zero), CustomData(anInt: 1, aString: "one", aCGPoint: CGPoint(x: 1, y: 1)) ]), SectionOfCustomData(header: "Second section", items: [CustomData(anInt: 2, aString: "two", aCGPoint: CGPoint(x: 2, y: 2)), CustomData(anInt: 3, aString: "three", aCGPoint: CGPoint(x: 3, y: 3)) ]) ] Observable.just(sections) .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) ``` ### Animations To implement animations with RxDataSources, the same steps are required as with non-animated data, execept: - SectionOfCustomData needs to conform to `AnimatableSectionModelType` - dataSource needs to be an instance of `RxTableViewSectionedAnimatedDataSource` or `RxCollectionViewSectionedAnimatedDataSource` ## Requirements Xcode 9.0 Swift 4.0 For Swift 3.x version please use versions `1.0 ... 2.0.2` For Swift 2.3 version please use versions `0.1 ... 0.9` ## Installation **We'll try to keep the API as stable as possible, but breaking API changes can occur.** ### CocoaPods Podfile ``` pod 'RxDataSources', '~> 3.0' ``` ### Carthage Cartfile ``` github "RxSwiftCommunity/RxDataSources" ~> 3.0 ``` ================================================ FILE: Pods/Differentiator/Sources/Differentiator/AnimatableSectionModel.swift ================================================ // // AnimatableSectionModel.swift // RxDataSources // // Created by Krunoslav Zaher on 1/10/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation public struct AnimatableSectionModel { public var model: Section public var items: [Item] public init(model: Section, items: [ItemType]) { self.model = model self.items = items } } extension AnimatableSectionModel : AnimatableSectionModelType { public typealias Item = ItemType public typealias Identity = Section.Identity public var identity: Section.Identity { return model.identity } public init(original: AnimatableSectionModel, items: [Item]) { self.model = original.model self.items = items } public var hashValue: Int { return self.model.identity.hashValue } } extension AnimatableSectionModel : CustomStringConvertible { public var description: String { return "HashableSectionModel(model: \"\(self.model)\", items: \(items))" } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/AnimatableSectionModelType+ItemPath.swift ================================================ // // AnimatableSectionModelType+ItemPath.swift // RxDataSources // // Created by Krunoslav Zaher on 1/9/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation extension Array where Element: AnimatableSectionModelType { subscript(index: ItemPath) -> Element.Item { return self[index.sectionIndex].items[index.itemIndex] } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/AnimatableSectionModelType.swift ================================================ // // AnimatableSectionModelType.swift // RxDataSources // // Created by Krunoslav Zaher on 1/6/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation public protocol AnimatableSectionModelType : SectionModelType , IdentifiableType where Item: IdentifiableType, Item: Equatable { } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/Changeset.swift ================================================ // // Changeset.swift // RxDataSources // // Created by Krunoslav Zaher on 5/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import Foundation public struct Changeset { public typealias I = S.Item public let reloadData: Bool public let originalSections: [S] public let finalSections: [S] public let insertedSections: [Int] public let deletedSections: [Int] public let movedSections: [(from: Int, to: Int)] public let updatedSections: [Int] public let insertedItems: [ItemPath] public let deletedItems: [ItemPath] public let movedItems: [(from: ItemPath, to: ItemPath)] public let updatedItems: [ItemPath] init(reloadData: Bool = false, originalSections: [S] = [], finalSections: [S] = [], insertedSections: [Int] = [], deletedSections: [Int] = [], movedSections: [(from: Int, to: Int)] = [], updatedSections: [Int] = [], insertedItems: [ItemPath] = [], deletedItems: [ItemPath] = [], movedItems: [(from: ItemPath, to: ItemPath)] = [], updatedItems: [ItemPath] = [] ) { self.reloadData = reloadData self.originalSections = originalSections self.finalSections = finalSections self.insertedSections = insertedSections self.deletedSections = deletedSections self.movedSections = movedSections self.updatedSections = updatedSections self.insertedItems = insertedItems self.deletedItems = deletedItems self.movedItems = movedItems self.updatedItems = updatedItems } public static func initialValue(_ sections: [S]) -> Changeset { return Changeset( reloadData: true, finalSections: sections, insertedSections: Array(0 ..< sections.count) as [Int] ) } } extension ItemPath : CustomDebugStringConvertible { public var debugDescription : String { return "(\(sectionIndex), \(itemIndex))" } } extension Changeset : CustomDebugStringConvertible { public var debugDescription : String { let serializedSections = "[\n" + finalSections.map { "\($0)" }.joined(separator: ",\n") + "\n]\n" return " >> Final sections" + " \n\(serializedSections)" + (insertedSections.count > 0 || deletedSections.count > 0 || movedSections.count > 0 || updatedSections.count > 0 ? "\nSections:" : "") + (insertedSections.count > 0 ? "\ninsertedSections:\n\t\(insertedSections)" : "") + (deletedSections.count > 0 ? "\ndeletedSections:\n\t\(deletedSections)" : "") + (movedSections.count > 0 ? "\nmovedSections:\n\t\(movedSections)" : "") + (updatedSections.count > 0 ? "\nupdatesSections:\n\t\(updatedSections)" : "") + (insertedItems.count > 0 || deletedItems.count > 0 || movedItems.count > 0 || updatedItems.count > 0 ? "\nItems:" : "") + (insertedItems.count > 0 ? "\ninsertedItems:\n\t\(insertedItems)" : "") + (deletedItems.count > 0 ? "\ndeletedItems:\n\t\(deletedItems)" : "") + (movedItems.count > 0 ? "\nmovedItems:\n\t\(movedItems)" : "") + (updatedItems.count > 0 ? "\nupdatedItems:\n\t\(updatedItems)" : "") } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/Diff.swift ================================================ // // Differentiator.swift // RxDataSources // // Created by Krunoslav Zaher on 6/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import Foundation fileprivate extension AnimatableSectionModelType { init(safeOriginal: Self, safeItems: [Item]) throws { self.init(original: safeOriginal, items: safeItems) if self.items != safeItems || self.identity != safeOriginal.identity { throw Diff.Error.invalidInitializerImplementation(section: self, expectedItems: safeItems, expectedIdentifier: safeOriginal.identity) } } } public enum Diff { public enum Error : Swift.Error, CustomDebugStringConvertible { case duplicateItem(item: Any) case duplicateSection(section: Any) case invalidInitializerImplementation(section: Any, expectedItems: Any, expectedIdentifier: Any) public var debugDescription: String { switch self { case let .duplicateItem(item): return "Duplicate item \(item)" case let .duplicateSection(section): return "Duplicate section \(section)" case let .invalidInitializerImplementation(section, expectedItems, expectedIdentifier): return "Wrong initializer implementation for: \(section)\n" + "Expected it should return items: \(expectedItems)\n" + "Expected it should have id: \(expectedIdentifier)" } } } private enum EditEvent : CustomDebugStringConvertible { case inserted // can't be found in old sections case insertedAutomatically // Item inside section being inserted case deleted // Was in old, not in new, in it's place is something "not new" :(, otherwise it's Updated case deletedAutomatically // Item inside section that is being deleted case moved // same item, but was on different index, and needs explicit move case movedAutomatically // don't need to specify any changes for those rows case untouched var debugDescription: String { get { switch self { case .inserted: return "Inserted" case .insertedAutomatically: return "InsertedAutomatically" case .deleted: return "Deleted" case .deletedAutomatically: return "DeletedAutomatically" case .moved: return "Moved" case .movedAutomatically: return "MovedAutomatically" case .untouched: return "Untouched" } } } } private struct SectionAssociatedData : CustomDebugStringConvertible { var event: EditEvent var indexAfterDelete: Int? var moveIndex: Int? var itemCount: Int var debugDescription: String { get { return "\(event), \(String(describing: indexAfterDelete))" } } static var initial: SectionAssociatedData { return SectionAssociatedData(event: .untouched, indexAfterDelete: nil, moveIndex: nil, itemCount: 0) } } private struct ItemAssociatedData: CustomDebugStringConvertible { var event: EditEvent var indexAfterDelete: Int? var moveIndex: ItemPath? var debugDescription: String { get { return "\(event) \(String(describing: indexAfterDelete))" } } static var initial : ItemAssociatedData { return ItemAssociatedData(event: .untouched, indexAfterDelete: nil, moveIndex: nil) } } private static func indexSections(_ sections: [S]) throws -> [S.Identity : Int] { var indexedSections: [S.Identity : Int] = [:] for (i, section) in sections.enumerated() { guard indexedSections[section.identity] == nil else { #if DEBUG if indexedSections[section.identity] != nil { print("Section \(section) has already been indexed at \(indexedSections[section.identity]!)") } #endif throw Error.duplicateSection(section: section) } indexedSections[section.identity] = i } return indexedSections } //================================================================================ // Optimizations because Swift dictionaries are extremely slow (ARC, bridging ...) //================================================================================ // swift dictionary optimizations { private struct OptimizedIdentity : Hashable { let hashValue: Int let identity: UnsafePointer init(_ identity: UnsafePointer) { self.identity = identity self.hashValue = identity.pointee.hashValue } static func == (lhs: OptimizedIdentity, rhs: OptimizedIdentity) -> Bool { if lhs.hashValue != rhs.hashValue { return false } if lhs.identity.distance(to: rhs.identity) == 0 { return true } return lhs.identity.pointee == rhs.identity.pointee } } private static func calculateAssociatedData( initialItemCache: ContiguousArray>, finalItemCache: ContiguousArray> ) throws -> (ContiguousArray>, ContiguousArray>) { typealias Identity = Item.Identity let totalInitialItems = initialItemCache.map { $0.count }.reduce(0, +) var initialIdentities: ContiguousArray = ContiguousArray() var initialItemPaths: ContiguousArray = ContiguousArray() initialIdentities.reserveCapacity(totalInitialItems) initialItemPaths.reserveCapacity(totalInitialItems) for (i, items) in initialItemCache.enumerated() { for j in 0 ..< items.count { let item = items[j] initialIdentities.append(item.identity) initialItemPaths.append(ItemPath(sectionIndex: i, itemIndex: j)) } } var initialItemData = ContiguousArray(initialItemCache.map { items in return ContiguousArray(repeating: ItemAssociatedData.initial, count: items.count) }) var finalItemData = ContiguousArray(finalItemCache.map { items in return ContiguousArray(repeating: ItemAssociatedData.initial, count: items.count) }) try initialIdentities.withUnsafeBufferPointer { (identitiesBuffer: UnsafeBufferPointer) -> () in var dictionary: [OptimizedIdentity: Int] = Dictionary(minimumCapacity: totalInitialItems * 2) for i in 0 ..< initialIdentities.count { let identityPointer = identitiesBuffer.baseAddress!.advanced(by: i) let key = OptimizedIdentity(identityPointer) if let existingValueItemPathIndex = dictionary[key] { let itemPath = initialItemPaths[existingValueItemPathIndex] let item = initialItemCache[itemPath.sectionIndex][itemPath.itemIndex] #if DEBUG print("Item \(item) has already been indexed at \(itemPath)" ) #endif throw Error.duplicateItem(item: item) } dictionary[key] = i } for (i, items) in finalItemCache.enumerated() { for j in 0 ..< items.count { let item = items[j] var identity = item.identity let key = OptimizedIdentity(&identity) guard let initialItemPathIndex = dictionary[key] else { continue } let itemPath = initialItemPaths[initialItemPathIndex] if initialItemData[itemPath.sectionIndex][itemPath.itemIndex].moveIndex != nil { throw Error.duplicateItem(item: item) } initialItemData[itemPath.sectionIndex][itemPath.itemIndex].moveIndex = ItemPath(sectionIndex: i, itemIndex: j) finalItemData[i][j].moveIndex = itemPath } } return () } return (initialItemData, finalItemData) } // } swift dictionary optimizations /* I've uncovered this case during random stress testing of logic. This is the hardest generic update case that causes two passes, first delete, and then move/insert [ NumberSection(model: "1", items: [1111]), NumberSection(model: "2", items: [2222]), ] [ NumberSection(model: "2", items: [0]), NumberSection(model: "1", items: []), ] If update is in the form * Move section from 2 to 1 * Delete Items at paths 0 - 0, 1 - 0 * Insert Items at paths 0 - 0 or * Move section from 2 to 1 * Delete Items at paths 0 - 0 * Reload Items at paths 1 - 0 or * Move section from 2 to 1 * Delete Items at paths 0 - 0 * Reload Items at paths 0 - 0 it crashes table view. No matter what change is performed, it fails for me. If anyone knows how to make this work for one Changeset, PR is welcome. */ // If you are considering working out your own algorithm, these are tricky // transition cases that you can use. // case 1 /* from = [ NumberSection(model: "section 4", items: [10, 11, 12]), NumberSection(model: "section 9", items: [25, 26, 27]), ] to = [ HashableSectionModel(model: "section 9", items: [11, 26, 27]), HashableSectionModel(model: "section 4", items: [10, 12]) ] */ // case 2 /* from = [ HashableSectionModel(model: "section 10", items: [26]), HashableSectionModel(model: "section 7", items: [5, 29]), HashableSectionModel(model: "section 1", items: [14]), HashableSectionModel(model: "section 5", items: [16]), HashableSectionModel(model: "section 4", items: []), HashableSectionModel(model: "section 8", items: [3, 15, 19, 23]), HashableSectionModel(model: "section 3", items: [20]) ] to = [ HashableSectionModel(model: "section 10", items: [26]), HashableSectionModel(model: "section 1", items: [14]), HashableSectionModel(model: "section 9", items: [3]), HashableSectionModel(model: "section 5", items: [16, 8]), HashableSectionModel(model: "section 8", items: [15, 19, 23]), HashableSectionModel(model: "section 3", items: [20]), HashableSectionModel(model: "Section 2", items: [7]) ] */ // case 3 /* from = [ HashableSectionModel(model: "section 4", items: [5]), HashableSectionModel(model: "section 6", items: [20, 14]), HashableSectionModel(model: "section 9", items: []), HashableSectionModel(model: "section 2", items: [2, 26]), HashableSectionModel(model: "section 8", items: [23]), HashableSectionModel(model: "section 10", items: [8, 18, 13]), HashableSectionModel(model: "section 1", items: [28, 25, 6, 11, 10, 29, 24, 7, 19]) ] to = [ HashableSectionModel(model: "section 4", items: [5]), HashableSectionModel(model: "section 6", items: [20, 14]), HashableSectionModel(model: "section 9", items: [16]), HashableSectionModel(model: "section 7", items: [17, 15, 4]), HashableSectionModel(model: "section 2", items: [2, 26, 23]), HashableSectionModel(model: "section 8", items: []), HashableSectionModel(model: "section 10", items: [8, 18, 13]), HashableSectionModel(model: "section 1", items: [28, 25, 6, 11, 10, 29, 24, 7, 19]) ] */ // Generates differential changes suitable for sectioned view consumption. // It will not only detect changes between two states, but it will also try to compress those changes into // almost minimal set of changes. // // I know, I know, it's ugly :( Totally agree, but this is the only general way I could find that works 100%, and // avoids UITableView quirks. // // Please take into consideration that I was also convinced about 20 times that I've found a simple general // solution, but then UITableView falls apart under stress testing :( // // Sincerely, if somebody else would present me this 250 lines of code, I would call him a mad man. I would think // that there has to be a simpler solution. Well, after 3 days, I'm not convinced any more :) // // Maybe it can be made somewhat simpler, but don't think it can be made much simpler. // // The algorithm could take anywhere from 1 to 3 table view transactions to finish the updates. // // * stage 1 - remove deleted sections and items // * stage 2 - move sections into place // * stage 3 - fix moved and new items // // There maybe exists a better division, but time will tell. // public static func differencesForSectionedView( initialSections: [S], finalSections: [S]) throws -> [Changeset] { typealias I = S.Item var result: [Changeset] = [] var sectionCommands = try CommandGenerator.generatorForInitialSections(initialSections, finalSections: finalSections) result.append(contentsOf: try sectionCommands.generateDeleteSectionsDeletedItemsAndUpdatedItems()) result.append(contentsOf: try sectionCommands.generateInsertAndMoveSections()) result.append(contentsOf: try sectionCommands.generateInsertAndMovedItems()) return result } @available(*, deprecated, renamed: "differencesForSectionedView(initialSections:finalSections:)") public static func differencesForSectionedView( _ initialSections: [S], finalSections: [S]) throws -> [Changeset] { return try differencesForSectionedView(initialSections: initialSections, finalSections: finalSections) } private struct CommandGenerator { typealias Item = S.Item let initialSections: [S] let finalSections: [S] let initialSectionData: ContiguousArray let finalSectionData: ContiguousArray let initialItemData: ContiguousArray> let finalItemData: ContiguousArray> let initialItemCache: ContiguousArray> let finalItemCache: ContiguousArray> static func generatorForInitialSections( _ initialSections: [S], finalSections: [S] ) throws -> CommandGenerator { let (initialSectionData, finalSectionData) = try calculateSectionMovements(initialSections: initialSections, finalSections: finalSections) let initialItemCache = ContiguousArray(initialSections.map { ContiguousArray($0.items) }) let finalItemCache = ContiguousArray(finalSections.map { ContiguousArray($0.items) }) let (initialItemData, finalItemData) = try calculateItemMovements( initialItemCache: initialItemCache, finalItemCache: finalItemCache, initialSectionData: initialSectionData, finalSectionData: finalSectionData ) return CommandGenerator( initialSections: initialSections, finalSections: finalSections, initialSectionData: initialSectionData, finalSectionData: finalSectionData, initialItemData: initialItemData, finalItemData: finalItemData, initialItemCache: initialItemCache, finalItemCache: finalItemCache ) } static func calculateItemMovements( initialItemCache: ContiguousArray>, finalItemCache: ContiguousArray>, initialSectionData: ContiguousArray, finalSectionData: ContiguousArray) throws -> (ContiguousArray>, ContiguousArray>) { var (initialItemData, finalItemData) = try Diff.calculateAssociatedData( initialItemCache: initialItemCache, finalItemCache: finalItemCache ) let findNextUntouchedOldIndex = { (initialSectionIndex: Int, initialSearchIndex: Int?) -> Int? in guard var i2 = initialSearchIndex else { return nil } while i2 < initialSectionData[initialSectionIndex].itemCount { if initialItemData[initialSectionIndex][i2].event == .untouched { return i2 } i2 = i2 + 1 } return nil } // first mark deleted items for i in 0 ..< initialItemCache.count { guard let _ = initialSectionData[i].moveIndex else { continue } var indexAfterDelete = 0 for j in 0 ..< initialItemCache[i].count { guard let finalIndexPath = initialItemData[i][j].moveIndex else { initialItemData[i][j].event = .deleted continue } // from this point below, section has to be move type because it's initial and not deleted // because there is no move to inserted section if finalSectionData[finalIndexPath.sectionIndex].event == .inserted { initialItemData[i][j].event = .deleted continue } initialItemData[i][j].indexAfterDelete = indexAfterDelete indexAfterDelete += 1 } } // mark moved or moved automatically for i in 0 ..< finalItemCache.count { guard let originalSectionIndex = finalSectionData[i].moveIndex else { continue } var untouchedIndex: Int? = 0 for j in 0 ..< finalItemCache[i].count { untouchedIndex = findNextUntouchedOldIndex(originalSectionIndex, untouchedIndex) guard let originalIndex = finalItemData[i][j].moveIndex else { finalItemData[i][j].event = .inserted continue } // In case trying to move from deleted section, abort, otherwise it will crash table view if initialSectionData[originalIndex.sectionIndex].event == .deleted { finalItemData[i][j].event = .inserted continue } // original section can't be inserted else if initialSectionData[originalIndex.sectionIndex].event == .inserted { try precondition(false, "New section in initial sections, that is wrong") } let initialSectionEvent = initialSectionData[originalIndex.sectionIndex].event try precondition(initialSectionEvent == .moved || initialSectionEvent == .movedAutomatically, "Section not moved") let eventType = originalIndex == ItemPath(sectionIndex: originalSectionIndex, itemIndex: untouchedIndex ?? -1) ? EditEvent.movedAutomatically : EditEvent.moved initialItemData[originalIndex.sectionIndex][originalIndex.itemIndex].event = eventType finalItemData[i][j].event = eventType } } return (initialItemData, finalItemData) } static func calculateSectionMovements(initialSections: [S], finalSections: [S]) throws -> (ContiguousArray, ContiguousArray) { let initialSectionIndexes = try Diff.indexSections(initialSections) var initialSectionData = ContiguousArray(repeating: SectionAssociatedData.initial, count: initialSections.count) var finalSectionData = ContiguousArray(repeating: SectionAssociatedData.initial, count: finalSections.count) for (i, section) in finalSections.enumerated() { finalSectionData[i].itemCount = finalSections[i].items.count guard let initialSectionIndex = initialSectionIndexes[section.identity] else { continue } if initialSectionData[initialSectionIndex].moveIndex != nil { throw Error.duplicateSection(section: section) } initialSectionData[initialSectionIndex].moveIndex = i finalSectionData[i].moveIndex = initialSectionIndex } var sectionIndexAfterDelete = 0 // deleted sections for i in 0 ..< initialSectionData.count { initialSectionData[i].itemCount = initialSections[i].items.count if initialSectionData[i].moveIndex == nil { initialSectionData[i].event = .deleted continue } initialSectionData[i].indexAfterDelete = sectionIndexAfterDelete sectionIndexAfterDelete += 1 } // moved sections var untouchedOldIndex: Int? = 0 let findNextUntouchedOldIndex = { (initialSearchIndex: Int?) -> Int? in guard var i = initialSearchIndex else { return nil } while i < initialSections.count { if initialSectionData[i].event == .untouched { return i } i = i + 1 } return nil } // inserted and moved sections { // this should fix all sections and move them into correct places // 2nd stage for i in 0 ..< finalSections.count { untouchedOldIndex = findNextUntouchedOldIndex(untouchedOldIndex) // oh, it did exist if let oldSectionIndex = finalSectionData[i].moveIndex { let moveType = oldSectionIndex != untouchedOldIndex ? EditEvent.moved : EditEvent.movedAutomatically finalSectionData[i].event = moveType initialSectionData[oldSectionIndex].event = moveType } else { finalSectionData[i].event = .inserted } } // inserted sections for (i, section) in finalSectionData.enumerated() { if section.moveIndex == nil { _ = finalSectionData[i].event == .inserted } } return (initialSectionData, finalSectionData) } mutating func generateDeleteSectionsDeletedItemsAndUpdatedItems() throws -> [Changeset] { var deletedSections = [Int]() var deletedItems = [ItemPath]() var updatedItems = [ItemPath]() var afterDeleteState = [S]() // mark deleted items { // 1rst stage again (I know, I know ...) for (i, initialItems) in initialItemCache.enumerated() { let event = initialSectionData[i].event // Deleted section will take care of deleting child items. // In case of moving an item from deleted section, tableview will // crash anyway, so this is not limiting anything. if event == .deleted { deletedSections.append(i) continue } var afterDeleteItems: [S.Item] = [] for j in 0 ..< initialItems.count { let event = initialItemData[i][j].event switch event { case .deleted: deletedItems.append(ItemPath(sectionIndex: i, itemIndex: j)) case .moved, .movedAutomatically: let finalItemIndex = try initialItemData[i][j].moveIndex.unwrap() let finalItem = finalItemCache[finalItemIndex.sectionIndex][finalItemIndex.itemIndex] if finalItem != initialSections[i].items[j] { updatedItems.append(ItemPath(sectionIndex: i, itemIndex: j)) } afterDeleteItems.append(finalItem) default: try precondition(false, "Unhandled case") } } afterDeleteState.append(try S.init(safeOriginal: initialSections[i], safeItems: afterDeleteItems)) } // } if deletedItems.count == 0 && deletedSections.count == 0 && updatedItems.count == 0 { return [] } return [Changeset( finalSections: afterDeleteState, deletedSections: deletedSections, deletedItems: deletedItems, updatedItems: updatedItems )] } func generateInsertAndMoveSections() throws -> [Changeset] { var movedSections = [(from: Int, to: Int)]() var insertedSections = [Int]() for i in 0 ..< initialSections.count { switch initialSectionData[i].event { case .deleted: break case .moved: movedSections.append((from: try initialSectionData[i].indexAfterDelete.unwrap(), to: try initialSectionData[i].moveIndex.unwrap())) case .movedAutomatically: break default: try precondition(false, "Unhandled case in initial sections") } } for i in 0 ..< finalSections.count { switch finalSectionData[i].event { case .inserted: insertedSections.append(i) default: break } } if insertedSections.count == 0 && movedSections.count == 0 { return [] } // sections should be in place, but items should be original without deleted ones let sectionsAfterChange: [S] = try self.finalSections.enumerated().map { i, s -> S in let event = self.finalSectionData[i].event if event == .inserted { // it's already set up return s } else if event == .moved || event == .movedAutomatically { let originalSectionIndex = try finalSectionData[i].moveIndex.unwrap() let originalSection = initialSections[originalSectionIndex] var items: [S.Item] = [] items.reserveCapacity(originalSection.items.count) let itemAssociatedData = self.initialItemData[originalSectionIndex] for j in 0 ..< originalSection.items.count { let initialData = itemAssociatedData[j] guard initialData.event != .deleted else { continue } guard let finalIndex = initialData.moveIndex else { try precondition(false, "Item was moved, but no final location.") continue } items.append(finalItemCache[finalIndex.sectionIndex][finalIndex.itemIndex]) } let modifiedSection = try S.init(safeOriginal: s, safeItems: items) return modifiedSection } else { try precondition(false, "This is weird, this shouldn't happen") return s } } return [Changeset( finalSections: sectionsAfterChange, insertedSections: insertedSections, movedSections: movedSections )] } mutating func generateInsertAndMovedItems() throws -> [Changeset] { var insertedItems = [ItemPath]() var movedItems = [(from: ItemPath, to: ItemPath)]() // mark new and moved items { // 3rd stage for i in 0 ..< finalSections.count { let finalSection = finalSections[i] let sectionEvent = finalSectionData[i].event // new and deleted sections cause reload automatically if sectionEvent != .moved && sectionEvent != .movedAutomatically { continue } for j in 0 ..< finalSection.items.count { let currentItemEvent = finalItemData[i][j].event try precondition(currentItemEvent != .untouched, "Current event is not untouched") let event = finalItemData[i][j].event switch event { case .inserted: insertedItems.append(ItemPath(sectionIndex: i, itemIndex: j)) case .moved: let originalIndex = try finalItemData[i][j].moveIndex.unwrap() let finalSectionIndex = try initialSectionData[originalIndex.sectionIndex].moveIndex.unwrap() let moveFromItemWithIndex = try initialItemData[originalIndex.sectionIndex][originalIndex.itemIndex].indexAfterDelete.unwrap() let moveCommand = ( from: ItemPath(sectionIndex: finalSectionIndex, itemIndex: moveFromItemWithIndex), to: ItemPath(sectionIndex: i, itemIndex: j) ) movedItems.append(moveCommand) default: break } } } // } if insertedItems.count == 0 && movedItems.count == 0 { return [] } return [Changeset( finalSections: finalSections, insertedItems: insertedItems, movedItems: movedItems )] } } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/IdentifiableType.swift ================================================ // // IdentifiableType.swift // RxDataSources // // Created by Krunoslav Zaher on 1/6/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation public protocol IdentifiableType { associatedtype Identity: Hashable var identity : Identity { get } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/IdentifiableValue.swift ================================================ // // IdentifiableValue.swift // RxDataSources // // Created by Krunoslav Zaher on 1/7/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation public struct IdentifiableValue { public let value: Value } extension IdentifiableValue : IdentifiableType { public typealias Identity = Value public var identity : Identity { return value } } extension IdentifiableValue : Equatable , CustomStringConvertible , CustomDebugStringConvertible { public var description: String { return "\(value)" } public var debugDescription: String { return "\(value)" } } public func == (lhs: IdentifiableValue, rhs: IdentifiableValue) -> Bool { return lhs.value == rhs.value } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/ItemPath.swift ================================================ // // ItemPath.swift // RxDataSources // // Created by Krunoslav Zaher on 1/9/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation public struct ItemPath { public let sectionIndex: Int public let itemIndex: Int public init(sectionIndex: Int, itemIndex: Int) { self.sectionIndex = sectionIndex self.itemIndex = itemIndex } } extension ItemPath : Equatable { } public func == (lhs: ItemPath, rhs: ItemPath) -> Bool { return lhs.sectionIndex == rhs.sectionIndex && lhs.itemIndex == rhs.itemIndex } extension ItemPath: Hashable { public var hashValue: Int { return sectionIndex.byteSwapped.hashValue ^ itemIndex.hashValue } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/Optional+Extensions.swift ================================================ // // Optional+Extensions.swift // RxDataSources // // Created by Krunoslav Zaher on 1/8/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation extension Optional { func unwrap() throws -> Wrapped { if let unwrapped = self { return unwrapped } else { debugFatalError("Error during unwrapping optional") throw DifferentiatorError.unwrappingOptional } } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/SectionModel.swift ================================================ // // SectionModel.swift // RxDataSources // // Created by Krunoslav Zaher on 6/16/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import Foundation public struct SectionModel { public var model: Section public var items: [Item] public init(model: Section, items: [Item]) { self.model = model self.items = items } } extension SectionModel : SectionModelType { public typealias Identity = Section public typealias Item = ItemType public var identity: Section { return model } } extension SectionModel : CustomStringConvertible { public var description: String { return "\(self.model) > \(items)" } } extension SectionModel { public init(original: SectionModel, items: [Item]) { self.model = original.model self.items = items } } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/SectionModelType.swift ================================================ // // SectionModelType.swift // RxDataSources // // Created by Krunoslav Zaher on 6/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import Foundation public protocol SectionModelType { associatedtype Item var items: [Item] { get } init(original: Self, items: [Item]) } ================================================ FILE: Pods/Differentiator/Sources/Differentiator/Utilities.swift ================================================ // // Utilities.swift // RxDataSources // // Created by muukii on 8/2/17. // Copyright © 2017 kzaher. All rights reserved. // import Foundation enum DifferentiatorError : Error { case unwrappingOptional case preconditionFailed(message: String) } func precondition(_ condition: Bool, _ message: @autoclosure() -> String) throws -> () { if condition { return } debugFatalError("Precondition failed") throw DifferentiatorError.preconditionFailed(message: message()) } func debugFatalError(_ error: Error) { debugFatalError("\(error)") } func debugFatalError(_ message: String) { #if DEBUG fatalError(message) #else print(message) #endif } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift ================================================ // // IQNSArray+Sort.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation import UIKit /** UIView.subviews sorting category. */ internal extension Array { ///-------------- /// MARK: Sorting ///-------------- /** Returns the array by sorting the UIView's by their tag property. */ internal func sortedArrayByTag() -> [Element] { return sorted(by: { (obj1 : Element, obj2 : Element) -> Bool in let view1 = obj1 as! UIView let view2 = obj2 as! UIView return (view1.tag < view2.tag) }) } /** Returns the array by sorting the UIView's by their tag property. */ internal func sortedArrayByPosition() -> [Element] { return sorted(by: { (obj1 : Element, obj2 : Element) -> Bool in let view1 = obj1 as! UIView let view2 = obj2 as! UIView let x1 = view1.frame.minX let y1 = view1.frame.minY let x2 = view2.frame.minX let y2 = view2.frame.minY if y1 != y2 { return y1 < y2 } else { return x1 < x2 } }) } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift ================================================ // // IQUIScrollView+Additions.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation import UIKit private var kIQShouldIgnoreScrollingAdjustment = "kIQShouldIgnoreScrollingAdjustment" private var kIQShouldRestoreScrollViewContentOffset = "kIQShouldRestoreScrollViewContentOffset" public extension UIScrollView { /** If YES, then scrollview will ignore scrolling (simply not scroll it) for adjusting textfield position. Default is NO. */ public var shouldIgnoreScrollingAdjustment: Bool { get { if let aValue = objc_getAssociatedObject(self, &kIQShouldIgnoreScrollingAdjustment) as? Bool { return aValue } else { return false } } set(newValue) { objc_setAssociatedObject(self, &kIQShouldIgnoreScrollingAdjustment, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } /** To set customized distance from keyboard for textField/textView. Can't be less than zero */ public var shouldRestoreScrollViewContentOffset: Bool { get { if let aValue = objc_getAssociatedObject(self, &kIQShouldRestoreScrollViewContentOffset) as? Bool { return aValue } else { return false } } set(newValue) { objc_setAssociatedObject(self, &kIQShouldRestoreScrollViewContentOffset, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift ================================================ // // IQUITextFieldView+Additions.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation import UIKit /** Uses default keyboard distance for textField. */ public let kIQUseDefaultKeyboardDistance = CGFloat.greatestFiniteMagnitude private var kIQKeyboardDistanceFromTextField = "kIQKeyboardDistanceFromTextField" private var kIQIgnoreSwitchingByNextPrevious = "kIQIgnoreSwitchingByNextPrevious" /** UIView category for managing UITextField/UITextView */ public extension UIView { /** To set customized distance from keyboard for textField/textView. Can't be less than zero */ public var keyboardDistanceFromTextField: CGFloat { get { if let aValue = objc_getAssociatedObject(self, &kIQKeyboardDistanceFromTextField) as? CGFloat { return aValue } else { return kIQUseDefaultKeyboardDistance } } set(newValue) { objc_setAssociatedObject(self, &kIQKeyboardDistanceFromTextField, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } /** If shouldIgnoreSwitchingByNextPrevious is true then library will ignore this textField/textView while moving to other textField/textView using keyboard toolbar next previous buttons. Default is false */ public var ignoreSwitchingByNextPrevious: Bool { get { if let aValue = objc_getAssociatedObject(self, &kIQIgnoreSwitchingByNextPrevious) as? Bool { return aValue } else { return false } } set(newValue) { objc_setAssociatedObject(self, &kIQIgnoreSwitchingByNextPrevious, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift ================================================ // // IQUIView+Hierarchy.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit /** UIView hierarchy category. */ public extension UIView { ///---------------------- /// MARK: viewControllers ///---------------------- /** Returns the UIViewController object that manages the receiver. */ public func viewController()->UIViewController? { var nextResponder: UIResponder? = self repeat { nextResponder = nextResponder?.next if let viewController = nextResponder as? UIViewController { return viewController } } while nextResponder != nil return nil } /** Returns the topMost UIViewController object in hierarchy. */ public func topMostController()->UIViewController? { var controllersHierarchy = [UIViewController]() if var topController = window?.rootViewController { controllersHierarchy.append(topController) while topController.presentedViewController != nil { topController = topController.presentedViewController! controllersHierarchy.append(topController) } var matchController :UIResponder? = viewController() while matchController != nil && controllersHierarchy.contains(matchController as! UIViewController) == false { repeat { matchController = matchController?.next } while matchController != nil && matchController is UIViewController == false } return matchController as? UIViewController } else { return viewController() } } ///----------------------------------- /// MARK: Superviews/Subviews/Siglings ///----------------------------------- /** Returns the superView of provided class type. */ public func superviewOfClassType(_ classType:UIView.Type)->UIView? { var superView = superview while let unwrappedSuperView = superView { if unwrappedSuperView.isKind(of: classType) { //If it's UIScrollView, then validating for special cases if unwrappedSuperView.isKind(of: UIScrollView.self) { let classNameString = NSStringFromClass(type(of:unwrappedSuperView.self)) // If it's not UITableViewWrapperView class, this is internal class which is actually manage in UITableview. The speciality of this class is that it's superview is UITableView. // If it's not UITableViewCellScrollView class, this is internal class which is actually manage in UITableviewCell. The speciality of this class is that it's superview is UITableViewCell. //If it's not _UIQueuingScrollView class, actually we validate for _ prefix which usually used by Apple internal classes if unwrappedSuperView.superview?.isKind(of: UITableView.self) == false && unwrappedSuperView.superview?.isKind(of: UITableViewCell.self) == false && classNameString.hasPrefix("_") == false { return superView } } else { return superView } } superView = unwrappedSuperView.superview } return nil } /** Returns all siblings of the receiver which canBecomeFirstResponder. */ public func responderSiblings()->[UIView] { //Array of (UITextField/UITextView's). var tempTextFields = [UIView]() // Getting all siblings if let siblings = superview?.subviews { for textField in siblings { if (textField == self || textField.ignoreSwitchingByNextPrevious == false) && textField._IQcanBecomeFirstResponder() == true { tempTextFields.append(textField) } } } return tempTextFields } /** Returns all deep subViews of the receiver which canBecomeFirstResponder. */ public func deepResponderViews()->[UIView] { //Array of (UITextField/UITextView's). var textfields = [UIView]() for textField in subviews { if (textField == self || textField.ignoreSwitchingByNextPrevious == false) && textField._IQcanBecomeFirstResponder() == true { textfields.append(textField) } //Sometimes there are hidden or disabled views and textField inside them still recorded, so we added some more validations here (Bug ID: #458) //Uncommented else (Bug ID: #625) if textField.subviews.count != 0 && isUserInteractionEnabled == true && isHidden == false && alpha != 0.0 { for deepView in textField.deepResponderViews() { textfields.append(deepView) } } } //subviews are returning in opposite order. Sorting according the frames 'y'. return textfields.sorted(by: { (view1 : UIView, view2 : UIView) -> Bool in let frame1 = view1.convert(view1.bounds, to: self) let frame2 = view2.convert(view2.bounds, to: self) let x1 = frame1.minX let y1 = frame1.minY let x2 = frame2.minX let y2 = frame2.minY if y1 != y2 { return y1 < y2 } else { return x1 < x2 } }) } fileprivate func _IQcanBecomeFirstResponder() -> Bool { var _IQcanBecomeFirstResponder = false // Setting toolbar to keyboard. if let textField = self as? UITextField { _IQcanBecomeFirstResponder = textField.isEnabled } else if let textView = self as? UITextView { _IQcanBecomeFirstResponder = textView.isEditable } if _IQcanBecomeFirstResponder == true { _IQcanBecomeFirstResponder = isUserInteractionEnabled == true && isHidden == false && alpha != 0.0 && isAlertViewTextField() == false && isSearchBarTextField() == false } return _IQcanBecomeFirstResponder } ///------------------------- /// MARK: Special TextFields ///------------------------- /** Returns YES if the receiver object is UISearchBarTextField, otherwise return NO. */ public func isSearchBarTextField()-> Bool { var searchBar : UIResponder? = self.next var isSearchBarTextField = false while searchBar != nil && isSearchBarTextField == false { if searchBar!.isKind(of: UISearchBar.self) { isSearchBarTextField = true break } else if searchBar is UIViewController { break } searchBar = searchBar?.next } return isSearchBarTextField } /** Returns YES if the receiver object is UIAlertSheetTextField, otherwise return NO. */ public func isAlertViewTextField()->Bool { var alertViewController : UIResponder? = self.viewController() var isAlertViewTextField = false while alertViewController != nil && isAlertViewTextField == false { if alertViewController!.isKind(of: UIAlertController.self) { isAlertViewTextField = true break } alertViewController = alertViewController?.next } return isAlertViewTextField } ///---------------- /// MARK: Transform ///---------------- /** Returns current view transform with respect to the 'toView'. */ public func convertTransformToView(_ toView:UIView?)->CGAffineTransform { var newView = toView if newView == nil { newView = window } //My Transform var myTransform = CGAffineTransform.identity if let superView = superview { myTransform = transform.concatenating(superView.convertTransformToView(nil)) } else { myTransform = transform } var viewTransform = CGAffineTransform.identity //view Transform if let unwrappedToView = newView { if let unwrappedSuperView = unwrappedToView.superview { viewTransform = unwrappedToView.transform.concatenating(unwrappedSuperView.convertTransformToView(nil)) } else { viewTransform = unwrappedToView.transform } } //Concating MyTransform and ViewTransform return myTransform.concatenating(viewTransform.inverted()) } ///----------------- /// TODO: Hierarchy ///----------------- // /** // Returns a string that represent the information about it's subview's hierarchy. You can use this method to debug the subview's positions. // */ // func subHierarchy()->NSString { // // } // // /** // Returns an string that represent the information about it's upper hierarchy. You can use this method to debug the superview's positions. // */ // func superHierarchy()->NSString { // // } // // /** // Returns an string that represent the information about it's frame positions. You can use this method to debug self positions. // */ // func debugHierarchy()->NSString { // // } fileprivate func depth()->Int { var depth : Int = 0 if let superView = superview { depth = superView.depth()+1 } return depth } } extension NSObject { public func _IQDescription() -> String { return "<\(self) \(Unmanaged.passUnretained(self).toOpaque())>" } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift ================================================ // // IQUIViewController+Additions.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit private var kIQLayoutGuideConstraint = "kIQLayoutGuideConstraint" public extension UIViewController { /** To set customized distance from keyboard for textField/textView. Can't be less than zero @deprecated Library is internally handling Safe Area (If you are using Safe Area from Xcode9 and iOS11) and there is no need to do any tweak if you already migrated to use Safe Area */ @available(iOS, deprecated: 11.0) @IBOutlet public var IQLayoutGuideConstraint: NSLayoutConstraint? { get { return objc_getAssociatedObject(self, &kIQLayoutGuideConstraint) as? NSLayoutConstraint } set(newValue) { objc_setAssociatedObject(self, &kIQLayoutGuideConstraint, newValue,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIWindow+Hierarchy.swift ================================================ // // IQUIWindow+Hierarchy.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit /** @abstract UIWindow hierarchy category. */ public extension UIWindow { /** @return Returns the current Top Most ViewController in hierarchy. */ public func topMostWindowController()->UIViewController? { var topController = rootViewController while let presentedController = topController?.presentedViewController { topController = presentedController } return topController } /** @return Returns the topViewController in stack of topMostWindowController. */ public func currentViewController()->UIViewController? { var currentViewController = topMostWindowController() while currentViewController != nil && currentViewController is UINavigationController && (currentViewController as! UINavigationController).topViewController != nil { currentViewController = (currentViewController as! UINavigationController).topViewController } return currentViewController } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift ================================================ // // IQKeyboardManagerConstants.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation ///----------------------------------- /// MARK: IQAutoToolbarManageBehaviour ///----------------------------------- /** `IQAutoToolbarBySubviews` Creates Toolbar according to subview's hirarchy of Textfield's in view. `IQAutoToolbarByTag` Creates Toolbar according to tag property of TextField's. `IQAutoToolbarByPosition` Creates Toolbar according to the y,x position of textField in it's superview coordinate. */ public enum IQAutoToolbarManageBehaviour : Int { case bySubviews case byTag case byPosition } /** `IQPreviousNextDisplayModeDefault` Show NextPrevious when there are more than 1 textField otherwise hide. `IQPreviousNextDisplayModeAlwaysHide` Do not show NextPrevious buttons in any case. `IQPreviousNextDisplayModeAlwaysShow` Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled. */ public enum IQPreviousNextDisplayMode : Int { case Default case alwaysHide case alwaysShow } /* /---------------------------------------------------------------------------------------------------\ \---------------------------------------------------------------------------------------------------/ | iOS Notification Mechanism | /---------------------------------------------------------------------------------------------------\ \---------------------------------------------------------------------------------------------------/ ------------------------------------------------------------ When UITextField become first responder ------------------------------------------------------------ - UITextFieldTextDidBeginEditingNotification (UITextField) - UIKeyboardWillShowNotification - UIKeyboardDidShowNotification ------------------------------------------------------------ When UITextView become first responder ------------------------------------------------------------ - UIKeyboardWillShowNotification - UITextViewTextDidBeginEditingNotification (UITextView) - UIKeyboardDidShowNotification ------------------------------------------------------------ When switching focus from UITextField to another UITextField ------------------------------------------------------------ - UITextFieldTextDidEndEditingNotification (UITextField1) - UITextFieldTextDidBeginEditingNotification (UITextField2) - UIKeyboardWillShowNotification - UIKeyboardDidShowNotification ------------------------------------------------------------ When switching focus from UITextView to another UITextView ------------------------------------------------------------ - UITextViewTextDidEndEditingNotification : (UITextView1) - UIKeyboardWillShowNotification - UITextViewTextDidBeginEditingNotification : (UITextView2) - UIKeyboardDidShowNotification ------------------------------------------------------------ When switching focus from UITextField to UITextView ------------------------------------------------------------ - UITextFieldTextDidEndEditingNotification (UITextField) - UIKeyboardWillShowNotification - UITextViewTextDidBeginEditingNotification (UITextView) - UIKeyboardDidShowNotification ------------------------------------------------------------ When switching focus from UITextView to UITextField ------------------------------------------------------------ - UITextViewTextDidEndEditingNotification (UITextView) - UITextFieldTextDidBeginEditingNotification (UITextField) - UIKeyboardWillShowNotification - UIKeyboardDidShowNotification ------------------------------------------------------------ When opening/closing UIKeyboard Predictive bar ------------------------------------------------------------ - UIKeyboardWillShowNotification - UIKeyboardDidShowNotification ------------------------------------------------------------ On orientation change ------------------------------------------------------------ - UIApplicationWillChangeStatusBarOrientationNotification - UIKeyboardWillHideNotification - UIKeyboardDidHideNotification - UIApplicationDidChangeStatusBarOrientationNotification - UIKeyboardWillShowNotification - UIKeyboardDidShowNotification - UIKeyboardWillShowNotification - UIKeyboardDidShowNotification */ ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift ================================================ // // IQKeyboardManagerConstantsInternal.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation ///----------------------------------- /// MARK: IQLayoutGuidePosition ///----------------------------------- /** `IQLayoutGuidePositionNone` If there are no IQLayoutGuideConstraint associated with viewController `IQLayoutGuidePositionTop` If provided IQLayoutGuideConstraint is associated with with viewController topLayoutGuide `IQLayoutGuidePositionBottom` If provided IQLayoutGuideConstraint is associated with with viewController bottomLayoutGuide */ enum IQLayoutGuidePosition : Int { case none case top case bottom } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift ================================================ // // IQKeyboardManager.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation import CoreGraphics import UIKit import QuartzCore ///--------------------- /// MARK: IQToolbar tags ///--------------------- /** Codeless drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. Neither need to write any code nor any setup required and much more. A generic version of KeyboardManagement. https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html */ open class IQKeyboardManager: NSObject, UIGestureRecognizerDelegate { /** Default tag for toolbar with Done button -1002. */ fileprivate static let kIQDoneButtonToolbarTag = -1002 /** Default tag for toolbar with Previous/Next buttons -1005. */ fileprivate static let kIQPreviousNextButtonToolbarTag = -1005 ///--------------------------- /// MARK: UIKeyboard handling ///--------------------------- /** Registered classes list with library. */ fileprivate var registeredClasses = [UIView.Type]() /** Enable/disable managing distance between keyboard and textField. Default is YES(Enabled when class loads in `+(void)load` method). */ open var enable = false { didSet { //If not enable, enable it. if enable == true && oldValue == false { //If keyboard is currently showing. Sending a fake notification for keyboardWillShow to adjust view according to keyboard. if _kbShowNotification != nil { keyboardWillShow(_kbShowNotification) } showLog("Enabled") } else if enable == false && oldValue == true { //If not disable, desable it. keyboardWillHide(nil) showLog("Disabled") } } } fileprivate func privateIsEnabled()-> Bool { var isEnabled = enable if let textFieldViewController = _textFieldView?.viewController() { if isEnabled == false { //If viewController is kind of enable viewController class, then assuming it's enabled. for enabledClass in enabledDistanceHandlingClasses { if textFieldViewController.isKind(of: enabledClass) { isEnabled = true break } } } if isEnabled == true { //If viewController is kind of disabled viewController class, then assuming it's disabled. for disabledClass in disabledDistanceHandlingClasses { if textFieldViewController.isKind(of: disabledClass) { isEnabled = false break } } //Special Controllers if isEnabled == true { let classNameString = NSStringFromClass(type(of:textFieldViewController.self)) //_UIAlertControllerTextFieldViewController if (classNameString.contains("UIAlertController") && classNameString.hasSuffix("TextFieldViewController")) { isEnabled = false } } } } return isEnabled } /** To set keyboard distance from textField. can't be less than zero. Default is 10.0. */ open var keyboardDistanceFromTextField: CGFloat { set { _privateKeyboardDistanceFromTextField = max(0, newValue) showLog("keyboardDistanceFromTextField: \(_privateKeyboardDistanceFromTextField)") } get { return _privateKeyboardDistanceFromTextField } } /** Boolean to know if keyboard is showing. */ open var keyboardShowing: Bool { get { return _privateIsKeyboardShowing } } /** moved distance to the top used to maintain distance between keyboard and textField. Most of the time this will be a positive value. */ open var movedDistance: CGFloat { get { return _privateMovedDistance } } /** Prevent keyboard manager to slide up the rootView to more than keyboard height. Default is YES. */ open var preventShowingBottomBlankSpace = true /** Returns the default singleton instance. */ @objc open class func sharedManager() -> IQKeyboardManager { struct Static { //Singleton instance. Initializing keyboard manger. static let kbManager = IQKeyboardManager() } /** @return Returns the default singleton instance. */ return Static.kbManager } ///------------------------- /// MARK: IQToolbar handling ///------------------------- /** Automatic add the IQToolbar functionality. Default is YES. */ open var enableAutoToolbar = true { didSet { privateIsEnableAutoToolbar() ?addToolbarIfRequired():removeToolbarIfRequired() let enableToolbar = enableAutoToolbar ? "Yes" : "NO" showLog("enableAutoToolbar: \(enableToolbar)") } } fileprivate func privateIsEnableAutoToolbar() -> Bool { var enableToolbar = enableAutoToolbar if let textFieldViewController = _textFieldView?.viewController() { if enableToolbar == false { //If found any toolbar enabled classes then return. for enabledClass in enabledToolbarClasses { if textFieldViewController.isKind(of: enabledClass) { enableToolbar = true break } } } if enableToolbar == true { //If found any toolbar disabled classes then return. for disabledClass in disabledToolbarClasses { if textFieldViewController.isKind(of: disabledClass) { enableToolbar = false break } } //Special Controllers if enableToolbar == true { let classNameString = NSStringFromClass(type(of:textFieldViewController.self)) //_UIAlertControllerTextFieldViewController if (classNameString.contains("UIAlertController") && classNameString.hasSuffix("TextFieldViewController")) { enableToolbar = false } } } } return enableToolbar } /** /** IQAutoToolbarBySubviews: Creates Toolbar according to subview's hirarchy of Textfield's in view. IQAutoToolbarByTag: Creates Toolbar according to tag property of TextField's. IQAutoToolbarByPosition: Creates Toolbar according to the y,x position of textField in it's superview coordinate. Default is IQAutoToolbarBySubviews. */ AutoToolbar managing behaviour. Default is IQAutoToolbarBySubviews. */ open var toolbarManageBehaviour = IQAutoToolbarManageBehaviour.bySubviews /** If YES, then uses textField's tintColor property for IQToolbar, otherwise tint color is black. Default is NO. */ open var shouldToolbarUsesTextFieldTintColor = false /** This is used for toolbar.tintColor when textfield.keyboardAppearance is UIKeyboardAppearanceDefault. If shouldToolbarUsesTextFieldTintColor is YES then this property is ignored. Default is nil and uses black color. */ open var toolbarTintColor : UIColor? /** This is used for toolbar.barTintColor. Default is nil and uses white color. */ open var toolbarBarTintColor : UIColor? /** IQPreviousNextDisplayModeDefault: Show NextPrevious when there are more than 1 textField otherwise hide. IQPreviousNextDisplayModeAlwaysHide: Do not show NextPrevious buttons in any case. IQPreviousNextDisplayModeAlwaysShow: Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled. */ open var previousNextDisplayMode = IQPreviousNextDisplayMode.Default /** Toolbar done button icon, If nothing is provided then check toolbarDoneBarButtonItemText to draw done button. */ open var toolbarDoneBarButtonItemImage : UIImage? /** Toolbar done button text, If nothing is provided then system default 'UIBarButtonSystemItemDone' will be used. */ open var toolbarDoneBarButtonItemText : String? /** If YES, then it add the textField's placeholder text on IQToolbar. Default is YES. */ @available(*,deprecated, message: "This is renamed to `shouldShowToolbarPlaceholder` for more clear naming.") open var shouldShowTextFieldPlaceholder: Bool { set { shouldShowToolbarPlaceholder = newValue } get { return shouldShowToolbarPlaceholder } } open var shouldShowToolbarPlaceholder = true /** Placeholder Font. Default is nil. */ open var placeholderFont: UIFont? ///-------------------------- /// MARK: UITextView handling ///-------------------------- /** used to adjust contentInset of UITextView. */ fileprivate var startingTextViewContentInsets = UIEdgeInsets.zero /** used to adjust scrollIndicatorInsets of UITextView. */ fileprivate var startingTextViewScrollIndicatorInsets = UIEdgeInsets.zero /** used with textView to detect a textFieldView contentInset is changed or not. (Bug ID: #92)*/ fileprivate var isTextViewContentInsetChanged = false ///--------------------------------------- /// MARK: UIKeyboard appearance overriding ///--------------------------------------- /** Override the keyboardAppearance for all textField/textView. Default is NO. */ open var overrideKeyboardAppearance = false /** If overrideKeyboardAppearance is YES, then all the textField keyboardAppearance is set using this property. */ open var keyboardAppearance = UIKeyboardAppearance.default ///----------------------------------------------------------- /// MARK: UITextField/UITextView Next/Previous/Resign handling ///----------------------------------------------------------- /** Resigns Keyboard on touching outside of UITextField/View. Default is NO. */ open var shouldResignOnTouchOutside = false { didSet { _tapGesture.isEnabled = privateShouldResignOnTouchOutside() let shouldResign = shouldResignOnTouchOutside ? "Yes" : "NO" showLog("shouldResignOnTouchOutside: \(shouldResign)") } } /** TapGesture to resign keyboard on view's touch. It's a readonly property and exposed only for adding/removing dependencies if your added gesture does have collision with this one */ fileprivate var _tapGesture: UITapGestureRecognizer! open var resignFirstResponderGesture: UITapGestureRecognizer { get { return _tapGesture } } /*******************************************/ fileprivate func privateShouldResignOnTouchOutside() -> Bool { var shouldResign = shouldResignOnTouchOutside if let textFieldViewController = _textFieldView?.viewController() { if shouldResign == false { //If viewController is kind of enable viewController class, then assuming shouldResignOnTouchOutside is enabled. for enabledClass in enabledTouchResignedClasses { if textFieldViewController.isKind(of: enabledClass) { shouldResign = true break } } } if shouldResign == true { //If viewController is kind of disable viewController class, then assuming shouldResignOnTouchOutside is disable. for disabledClass in disabledTouchResignedClasses { if textFieldViewController.isKind(of: disabledClass) { shouldResign = false break } } //Special Controllers if shouldResign == true { let classNameString = NSStringFromClass(type(of:textFieldViewController.self)) //_UIAlertControllerTextFieldViewController if (classNameString.contains("UIAlertController") && classNameString.hasSuffix("TextFieldViewController")) { shouldResign = false } } } } return shouldResign } /** Resigns currently first responder field. */ @discardableResult open func resignFirstResponder()-> Bool { if let textFieldRetain = _textFieldView { //Resigning first responder let isResignFirstResponder = textFieldRetain.resignFirstResponder() // If it refuses then becoming it as first responder again. (Bug ID: #96) if isResignFirstResponder == false { //If it refuses to resign then becoming it first responder again for getting notifications callback. textFieldRetain.becomeFirstResponder() showLog("Refuses to resign first responder: \(String(describing: _textFieldView?._IQDescription()))") } return isResignFirstResponder } return false } /** Returns YES if can navigate to previous responder textField/textView, otherwise NO. */ @objc open var canGoPrevious: Bool { //Getting all responder view's. if let textFields = responderViews() { if let textFieldRetain = _textFieldView { //Getting index of current textField. if let index = textFields.index(of: textFieldRetain) { //If it is not first textField. then it's previous object canBecomeFirstResponder. if index > 0 { return true } } } } return false } /** Returns YES if can navigate to next responder textField/textView, otherwise NO. */ @objc open var canGoNext: Bool { //Getting all responder view's. if let textFields = responderViews() { if let textFieldRetain = _textFieldView { //Getting index of current textField. if let index = textFields.index(of: textFieldRetain) { //If it is not first textField. then it's previous object canBecomeFirstResponder. if index < textFields.count-1 { return true } } } } return false } /** Navigate to previous responder textField/textView. */ @objc @discardableResult open func goPrevious()-> Bool { //Getting all responder view's. if let textFieldRetain = _textFieldView { if let textFields = responderViews() { //Getting index of current textField. if let index = textFields.index(of: textFieldRetain) { //If it is not first textField. then it's previous object becomeFirstResponder. if index > 0 { let nextTextField = textFields[index-1] let isAcceptAsFirstResponder = nextTextField.becomeFirstResponder() // If it refuses then becoming previous textFieldView as first responder again. (Bug ID: #96) if isAcceptAsFirstResponder == false { //If next field refuses to become first responder then restoring old textField as first responder. textFieldRetain.becomeFirstResponder() showLog("Refuses to become first responder: \(nextTextField._IQDescription())") } return isAcceptAsFirstResponder } } } } return false } /** Navigate to next responder textField/textView. */ @objc @discardableResult open func goNext()-> Bool { //Getting all responder view's. if let textFieldRetain = _textFieldView { if let textFields = responderViews() { //Getting index of current textField. if let index = textFields.index(of: textFieldRetain) { //If it is not last textField. then it's next object becomeFirstResponder. if index < textFields.count-1 { let nextTextField = textFields[index+1] let isAcceptAsFirstResponder = nextTextField.becomeFirstResponder() // If it refuses then becoming previous textFieldView as first responder again. (Bug ID: #96) if isAcceptAsFirstResponder == false { //If next field refuses to become first responder then restoring old textField as first responder. textFieldRetain.becomeFirstResponder() showLog("Refuses to become first responder: \(nextTextField._IQDescription())") } return isAcceptAsFirstResponder } } } } return false } /** previousAction. */ @objc internal func previousAction (_ barButton : IQBarButtonItem) { //If user wants to play input Click sound. if shouldPlayInputClicks == true { //Play Input Click Sound. UIDevice.current.playInputClick() } if canGoPrevious == true { if let textFieldRetain = _textFieldView { let isAcceptAsFirstResponder = goPrevious() if isAcceptAsFirstResponder && barButton.invocation.target != nil && barButton.invocation.action != nil { UIApplication.shared.sendAction(barButton.invocation.action!, to: barButton.invocation.target, from: textFieldRetain, for: UIEvent()) } } } } /** nextAction. */ @objc internal func nextAction (_ barButton : IQBarButtonItem) { //If user wants to play input Click sound. if shouldPlayInputClicks == true { //Play Input Click Sound. UIDevice.current.playInputClick() } if canGoNext == true { if let textFieldRetain = _textFieldView { let isAcceptAsFirstResponder = goNext() if isAcceptAsFirstResponder && barButton.invocation.target != nil && barButton.invocation.action != nil { UIApplication.shared.sendAction(barButton.invocation.action!, to: barButton.invocation.target, from: textFieldRetain, for: UIEvent()) } } } } /** doneAction. Resigning current textField. */ @objc internal func doneAction (_ barButton : IQBarButtonItem) { //If user wants to play input Click sound. if shouldPlayInputClicks == true { //Play Input Click Sound. UIDevice.current.playInputClick() } if let textFieldRetain = _textFieldView { //Resign textFieldView. let isResignedFirstResponder = resignFirstResponder() if isResignedFirstResponder && barButton.invocation.target != nil && barButton.invocation.action != nil{ UIApplication.shared.sendAction(barButton.invocation.action!, to: barButton.invocation.target, from: textFieldRetain, for: UIEvent()) } } } /** Resigning on tap gesture. (Enhancement ID: #14)*/ @objc internal func tapRecognized(_ gesture: UITapGestureRecognizer) { if gesture.state == UIGestureRecognizerState.ended { //Resigning currently responder textField. _ = resignFirstResponder() } } /** Note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES. */ open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return false } /** To not detect touch events in a subclass of UIControl, these may have added their own selector for specific work */ open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { // Should not recognize gesture if the clicked view is either UIControl or UINavigationBar(=3.2) ///------------------------------------ /// MARK: Safe Area ///------------------------------------ /** If YES, then library will try to adjust viewController.additionalSafeAreaInsets to automatically handle layout guide. Default is NO. */ open var canAdjustAdditionalSafeAreaInsets = false #endif ///------------------------------------ /// MARK: Class Level disabling methods ///------------------------------------ /** Disable distance handling within the scope of disabled distance handling viewControllers classes. Within this scope, 'enabled' property is ignored. Class should be kind of UIViewController. */ open var disabledDistanceHandlingClasses = [UIViewController.Type]() /** Enable distance handling within the scope of enabled distance handling viewControllers classes. Within this scope, 'enabled' property is ignored. Class should be kind of UIViewController. If same Class is added in disabledDistanceHandlingClasses list, then enabledDistanceHandlingClasses will be ignored. */ open var enabledDistanceHandlingClasses = [UIViewController.Type]() /** Disable automatic toolbar creation within the scope of disabled toolbar viewControllers classes. Within this scope, 'enableAutoToolbar' property is ignored. Class should be kind of UIViewController. */ open var disabledToolbarClasses = [UIViewController.Type]() /** Enable automatic toolbar creation within the scope of enabled toolbar viewControllers classes. Within this scope, 'enableAutoToolbar' property is ignored. Class should be kind of UIViewController. If same Class is added in disabledToolbarClasses list, then enabledToolbarClasses will be ignore. */ open var enabledToolbarClasses = [UIViewController.Type]() /** Allowed subclasses of UIView to add all inner textField, this will allow to navigate between textField contains in different superview. Class should be kind of UIView. */ open var toolbarPreviousNextAllowedClasses = [UIView.Type]() /** Disabled classes to ignore 'shouldResignOnTouchOutside' property, Class should be kind of UIViewController. */ open var disabledTouchResignedClasses = [UIViewController.Type]() /** Enabled classes to forcefully enable 'shouldResignOnTouchOutsite' property. Class should be kind of UIViewController. If same Class is added in disabledTouchResignedClasses list, then enabledTouchResignedClasses will be ignored. */ open var enabledTouchResignedClasses = [UIViewController.Type]() /** if shouldResignOnTouchOutside is enabled then you can customise the behaviour to not recognise gesture touches on some specific view subclasses. Class should be kind of UIView. Default is [UIControl, UINavigationBar] */ open var touchResignedGestureIgnoreClasses = [UIView.Type]() ///------------------------------------------- /// MARK: Third Party Library support /// Add TextField/TextView Notifications customised Notifications. For example while using YYTextView https://github.com/ibireme/YYText ///------------------------------------------- /** Add/Remove customised Notification for third party customised TextField/TextView. Please be aware that the Notification object must be idential to UITextField/UITextView Notification objects and customised TextField/TextView support must be idential to UITextField/UITextView. @param didBeginEditingNotificationName This should be identical to UITextViewTextDidBeginEditingNotification @param didEndEditingNotificationName This should be identical to UITextViewTextDidEndEditingNotification */ open func registerTextFieldViewClass(_ aClass: UIView.Type, didBeginEditingNotificationName : String, didEndEditingNotificationName : String) { registeredClasses.append(aClass) NotificationCenter.default.addObserver(self, selector: #selector(self.textFieldViewDidBeginEditing(_:)), name: Notification.Name(rawValue: didBeginEditingNotificationName), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.textFieldViewDidEndEditing(_:)), name: Notification.Name(rawValue: didEndEditingNotificationName), object: nil) } open func unregisterTextFieldViewClass(_ aClass: UIView.Type, didBeginEditingNotificationName : String, didEndEditingNotificationName : String) { if let index = registeredClasses.index(where: { element in return element == aClass.self }) { registeredClasses.remove(at: index) } NotificationCenter.default.removeObserver(self, name: Notification.Name(rawValue: didBeginEditingNotificationName), object: nil) NotificationCenter.default.removeObserver(self, name: Notification.Name(rawValue: didEndEditingNotificationName), object: nil) } /**************************************************************************************/ ///------------------------ /// MARK: Private variables ///------------------------ /*******************************************/ /** To save UITextField/UITextView object voa textField/textView notifications. */ fileprivate weak var _textFieldView: UIView? /** To save rootViewController.view.frame. */ fileprivate var _topViewBeginRect = CGRect.zero /** To save rootViewController */ fileprivate weak var _rootViewController: UIViewController? #if swift(>=3.2) /** To save additionalSafeAreaInsets of rootViewController to tweak iOS11 Safe Area */ fileprivate var _initialAdditionalSafeAreaInsets = UIEdgeInsets.zero #endif /** To save topBottomLayoutConstraint original constant */ fileprivate var _layoutGuideConstraintInitialConstant: CGFloat = 0 /** To save topBottomLayoutConstraint original constraint reference */ fileprivate weak var _layoutGuideConstraint: NSLayoutConstraint? /*******************************************/ /** Variable to save lastScrollView that was scrolled. */ fileprivate weak var _lastScrollView: UIScrollView? /** LastScrollView's initial contentOffset. */ fileprivate var _startingContentOffset = CGPoint.zero /** LastScrollView's initial scrollIndicatorInsets. */ fileprivate var _startingScrollIndicatorInsets = UIEdgeInsets.zero /** LastScrollView's initial contentInsets. */ fileprivate var _startingContentInsets = UIEdgeInsets.zero /*******************************************/ /** To save keyboardWillShowNotification. Needed for enable keyboard functionality. */ fileprivate var _kbShowNotification: Notification? /** To save keyboard size. */ fileprivate var _kbSize = CGSize.zero /** To save Status Bar size. */ fileprivate var _statusBarFrame = CGRect.zero /** To save keyboard animation duration. */ fileprivate var _animationDuration : TimeInterval = 0.25 /** To mimic the keyboard animation */ fileprivate var _animationCurve = UIViewAnimationOptions.curveEaseOut /*******************************************/ /** Boolean to maintain keyboard is showing or it is hide. To solve rootViewController.view.frame calculations. */ fileprivate var _privateIsKeyboardShowing = false fileprivate var _privateMovedDistance : CGFloat = 0.0 /** To use with keyboardDistanceFromTextField. */ fileprivate var _privateKeyboardDistanceFromTextField: CGFloat = 10.0 /**************************************************************************************/ ///-------------------------------------- /// MARK: Initialization/Deinitialization ///-------------------------------------- /* Singleton Object Initialization. */ override init() { super.init() self.registerAllNotifications() //Creating gesture for @shouldResignOnTouchOutside. (Enhancement ID: #14) _tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapRecognized(_:))) _tapGesture.cancelsTouchesInView = false _tapGesture.delegate = self _tapGesture.isEnabled = shouldResignOnTouchOutside //Loading IQToolbar, IQTitleBarButtonItem, IQBarButtonItem to fix first time keyboard appearance delay (Bug ID: #550) let textField = UITextField() textField.addDoneOnKeyboardWithTarget(nil, action: #selector(self.doneAction(_:))) textField.addPreviousNextDoneOnKeyboardWithTarget(nil, previousAction: #selector(self.previousAction(_:)), nextAction: #selector(self.nextAction(_:)), doneAction: #selector(self.doneAction(_:))) disabledDistanceHandlingClasses.append(UITableViewController.self) disabledDistanceHandlingClasses.append(UIAlertController.self) disabledToolbarClasses.append(UIAlertController.self) disabledTouchResignedClasses.append(UIAlertController.self) toolbarPreviousNextAllowedClasses.append(UITableView.self) toolbarPreviousNextAllowedClasses.append(UICollectionView.self) toolbarPreviousNextAllowedClasses.append(IQPreviousNextView.self) touchResignedGestureIgnoreClasses.append(UIControl.self) touchResignedGestureIgnoreClasses.append(UINavigationBar.self) } /** Override +load method to enable KeyboardManager when class loader load IQKeyboardManager. Enabling when app starts (No need to write any code) */ /** It doesn't work from Swift 1.2 */ // override public class func load() { // super.load() // // //Enabling IQKeyboardManager. // IQKeyboardManager.sharedManager().enable = true // } deinit { // Disable the keyboard manager. enable = false //Removing notification observers on dealloc. NotificationCenter.default.removeObserver(self) } /** Getting keyWindow. */ fileprivate func keyWindow() -> UIWindow? { if let keyWindow = _textFieldView?.window { return keyWindow } else { struct Static { /** @abstract Save keyWindow object for reuse. @discussion Sometimes [[UIApplication sharedApplication] keyWindow] is returning nil between the app. */ static weak var keyWindow : UIWindow? } /* (Bug ID: #23, #25, #73) */ let originalKeyWindow = UIApplication.shared.keyWindow //If original key window is not nil and the cached keywindow is also not original keywindow then changing keywindow. if originalKeyWindow != nil && (Static.keyWindow == nil || Static.keyWindow != originalKeyWindow) { Static.keyWindow = originalKeyWindow } //Return KeyWindow return Static.keyWindow } } ///----------------------- /// MARK: Helper Functions ///----------------------- /* Helper function to manipulate RootViewController's frame with animation. */ fileprivate func setRootViewFrame(_ frame: CGRect) { // Getting topMost ViewController. var controller = _textFieldView?.topMostController() if controller == nil { controller = keyWindow()?.topMostWindowController() } if let unwrappedController = controller { var newFrame = frame //frame size needs to be adjusted on iOS8 due to orientation structure changes. newFrame.size = unwrappedController.view.frame.size #if swift(>=3.2) var safeAreaNewInset = UIEdgeInsets.zero; if canAdjustAdditionalSafeAreaInsets { if #available(iOS 11, *) { if let textFieldView = _textFieldView { safeAreaNewInset = _initialAdditionalSafeAreaInsets; let viewMovement : CGFloat = _topViewBeginRect.maxY - newFrame.maxY; //Maintain keyboardDistanceFromTextField var specialKeyboardDistanceFromTextField = textFieldView.keyboardDistanceFromTextField if textFieldView.isSearchBarTextField() { if let searchBar = textFieldView.superviewOfClassType(UISearchBar.self) { specialKeyboardDistanceFromTextField = searchBar.keyboardDistanceFromTextField } } let newKeyboardDistanceFromTextField = (specialKeyboardDistanceFromTextField == kIQUseDefaultKeyboardDistance) ? keyboardDistanceFromTextField : specialKeyboardDistanceFromTextField let textFieldDistance = textFieldView.frame.size.height + newKeyboardDistanceFromTextField; safeAreaNewInset.bottom += min(viewMovement, textFieldDistance); } } } #endif //Used UIViewAnimationOptionBeginFromCurrentState to minimize strange animations. UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in #if swift(>=3.2) if self.canAdjustAdditionalSafeAreaInsets { if #available(iOS 11, *) { unwrappedController.additionalSafeAreaInsets = safeAreaNewInset; } } #endif // Setting it's new frame unwrappedController.view.frame = newFrame self.showLog("Set \(String(describing: controller?._IQDescription())) frame to : \(newFrame)") //Animating content if needed (Bug ID: #204) if self.layoutIfNeededOnUpdate == true { //Animating content (Bug ID: #160) unwrappedController.view.setNeedsLayout() unwrappedController.view.layoutIfNeeded() } }) { (animated:Bool) -> Void in} } else { // If can't get rootViewController then printing warning to user. showLog("You must set UIWindow.rootViewController in your AppDelegate to work with IQKeyboardManager") } } /* Adjusting RootViewController's frame according to interface orientation. */ fileprivate func adjustFrame() { // We are unable to get textField object while keyboard showing on UIWebView's textField. (Bug ID: #11) if _textFieldView == nil { return } let textFieldView = _textFieldView! let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") // Getting KeyWindow object. let optionalWindow = keyWindow() // Getting RootViewController. (Bug ID: #1, #4) var optionalRootController = _textFieldView?.topMostController() if optionalRootController == nil { optionalRootController = keyWindow()?.topMostWindowController() } // Converting Rectangle according to window bounds. let optionalTextFieldViewRect = textFieldView.superview?.convert(textFieldView.frame, to: optionalWindow) if optionalRootController == nil || optionalWindow == nil || optionalTextFieldViewRect == nil { return } let rootController = optionalRootController! let window = optionalWindow! let textFieldViewRect = optionalTextFieldViewRect! // Getting RootViewRect. var rootViewRect = rootController.view.frame //Getting statusBarFrame //Maintain keyboardDistanceFromTextField var specialKeyboardDistanceFromTextField = textFieldView.keyboardDistanceFromTextField if textFieldView.isSearchBarTextField() { if let searchBar = textFieldView.superviewOfClassType(UISearchBar.self) { specialKeyboardDistanceFromTextField = searchBar.keyboardDistanceFromTextField } } let newKeyboardDistanceFromTextField = (specialKeyboardDistanceFromTextField == kIQUseDefaultKeyboardDistance) ? keyboardDistanceFromTextField : specialKeyboardDistanceFromTextField var kbSize = _kbSize kbSize.height += newKeyboardDistanceFromTextField let statusBarFrame = UIApplication.shared.statusBarFrame // (Bug ID: #250) var layoutGuidePosition = IQLayoutGuidePosition.none if let viewController = textFieldView.viewController() { if let constraint = _layoutGuideConstraint { var layoutGuide : UILayoutSupport? if let itemLayoutGuide = constraint.firstItem as? UILayoutSupport { layoutGuide = itemLayoutGuide } else if let itemLayoutGuide = constraint.secondItem as? UILayoutSupport { layoutGuide = itemLayoutGuide } if let itemLayoutGuide : UILayoutSupport = layoutGuide { if (itemLayoutGuide === viewController.topLayoutGuide) //If topLayoutGuide constraint { layoutGuidePosition = .top } else if (itemLayoutGuide === viewController.bottomLayoutGuide) //If bottomLayoutGuice constraint { layoutGuidePosition = .bottom } } } } let topLayoutGuide : CGFloat = statusBarFrame.height var move : CGFloat = 0.0 // Move positive = textField is hidden. // Move negative = textField is showing. // Checking if there is bottomLayoutGuide attached (Bug ID: #250) if layoutGuidePosition == .bottom { // Calculating move position. move = textFieldViewRect.maxY-(window.frame.height-kbSize.height) } else { // Calculating move position. Common for both normal and special cases. move = min(textFieldViewRect.minY-(topLayoutGuide+5), textFieldViewRect.maxY-(window.frame.height-kbSize.height)) } showLog("Need to move: \(move)") var superScrollView : UIScrollView? = nil var superView = textFieldView.superviewOfClassType(UIScrollView.self) as? UIScrollView //Getting UIScrollView whose scrolling is enabled. // (Bug ID: #285) while let view = superView { if (view.isScrollEnabled && view.shouldIgnoreScrollingAdjustment == false) { superScrollView = view break } else { // Getting it's superScrollView. // (Enhancement ID: #21, #24) superView = view.superviewOfClassType(UIScrollView.self) as? UIScrollView } } //If there was a lastScrollView. // (Bug ID: #34) if let lastScrollView = _lastScrollView { //If we can't find current superScrollView, then setting lastScrollView to it's original form. if superScrollView == nil { showLog("Restoring \(lastScrollView._IQDescription()) contentInset to : \(_startingContentInsets) and contentOffset to : \(_startingContentOffset)") UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in lastScrollView.contentInset = self._startingContentInsets lastScrollView.scrollIndicatorInsets = self._startingScrollIndicatorInsets }) { (animated:Bool) -> Void in } if lastScrollView.shouldRestoreScrollViewContentOffset == true { lastScrollView.setContentOffset(_startingContentOffset, animated: true) } _startingContentInsets = UIEdgeInsets.zero _startingScrollIndicatorInsets = UIEdgeInsets.zero _startingContentOffset = CGPoint.zero _lastScrollView = nil } else if superScrollView != lastScrollView { //If both scrollView's are different, then reset lastScrollView to it's original frame and setting current scrollView as last scrollView. showLog("Restoring \(lastScrollView._IQDescription()) contentInset to : \(_startingContentInsets) and contentOffset to : \(_startingContentOffset)") UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in lastScrollView.contentInset = self._startingContentInsets lastScrollView.scrollIndicatorInsets = self._startingScrollIndicatorInsets }) { (animated:Bool) -> Void in } if lastScrollView.shouldRestoreScrollViewContentOffset == true { lastScrollView.setContentOffset(_startingContentOffset, animated: true) } _lastScrollView = superScrollView _startingContentInsets = superScrollView!.contentInset _startingScrollIndicatorInsets = superScrollView!.scrollIndicatorInsets _startingContentOffset = superScrollView!.contentOffset showLog("Saving New \(lastScrollView._IQDescription()) contentInset : \(_startingContentInsets) and contentOffset : \(_startingContentOffset)") } //Else the case where superScrollView == lastScrollView means we are on same scrollView after switching to different textField. So doing nothing, going ahead } else if let unwrappedSuperScrollView = superScrollView { //If there was no lastScrollView and we found a current scrollView. then setting it as lastScrollView. _lastScrollView = unwrappedSuperScrollView _startingContentInsets = unwrappedSuperScrollView.contentInset _startingScrollIndicatorInsets = unwrappedSuperScrollView.scrollIndicatorInsets _startingContentOffset = unwrappedSuperScrollView.contentOffset showLog("Saving \(unwrappedSuperScrollView._IQDescription()) contentInset : \(_startingContentInsets) and contentOffset : \(_startingContentOffset)") } // Special case for ScrollView. // If we found lastScrollView then setting it's contentOffset to show textField. if let lastScrollView = _lastScrollView { //Saving var lastView = textFieldView var superScrollView = _lastScrollView while let scrollView = superScrollView { //Looping in upper hierarchy until we don't found any scrollView in it's upper hirarchy till UIWindow object. if move > 0 ? (move > (-scrollView.contentOffset.y - scrollView.contentInset.top)) : scrollView.contentOffset.y>0 { var tempScrollView = scrollView.superviewOfClassType(UIScrollView.self) as? UIScrollView var nextScrollView : UIScrollView? = nil while let view = tempScrollView { if (view.isScrollEnabled && view.shouldIgnoreScrollingAdjustment == false) { nextScrollView = view break } else { tempScrollView = view.superviewOfClassType(UIScrollView.self) as? UIScrollView } } //Getting lastViewRect. if let lastViewRect = lastView.superview?.convert(lastView.frame, to: scrollView) { //Calculating the expected Y offset from move and scrollView's contentOffset. var shouldOffsetY = scrollView.contentOffset.y - min(scrollView.contentOffset.y,-move) //Rearranging the expected Y offset according to the view. shouldOffsetY = min(shouldOffsetY, lastViewRect.origin.y /*-5*/) //-5 is for good UI.//Commenting -5 (Bug ID: #69) //[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type //nextScrollView == nil If processing scrollView is last scrollView in upper hierarchy (there is no other scrollView upper hierrchy.) //[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type //shouldOffsetY >= 0 shouldOffsetY must be greater than in order to keep distance from navigationBar (Bug ID: #92) if textFieldView is UITextView == true && nextScrollView == nil && shouldOffsetY >= 0 { var maintainTopLayout : CGFloat = 0 if let navigationBarFrame = textFieldView.viewController()?.navigationController?.navigationBar.frame { maintainTopLayout = navigationBarFrame.maxY } maintainTopLayout += 10.0 //For good UI // Converting Rectangle according to window bounds. if let currentTextFieldViewRect = textFieldView.superview?.convert(textFieldView.frame, to: window) { //Calculating expected fix distance which needs to be managed from navigation bar let expectedFixDistance = currentTextFieldViewRect.minY - maintainTopLayout //Now if expectedOffsetY (superScrollView.contentOffset.y + expectedFixDistance) is lower than current shouldOffsetY, which means we're in a position where navigationBar up and hide, then reducing shouldOffsetY with expectedOffsetY (superScrollView.contentOffset.y + expectedFixDistance) shouldOffsetY = min(shouldOffsetY, scrollView.contentOffset.y + expectedFixDistance) //Setting move to 0 because now we don't want to move any view anymore (All will be managed by our contentInset logic. move = 0 } else { //Subtracting the Y offset from the move variable, because we are going to change scrollView's contentOffset.y to shouldOffsetY. move -= (shouldOffsetY-scrollView.contentOffset.y) } } else { //Subtracting the Y offset from the move variable, because we are going to change scrollView's contentOffset.y to shouldOffsetY. move -= (shouldOffsetY-scrollView.contentOffset.y) } //Getting problem while using `setContentOffset:animated:`, So I used animation API. UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in self.showLog("Adjusting \(scrollView.contentOffset.y-shouldOffsetY) to \(scrollView._IQDescription()) ContentOffset") self.showLog("Remaining Move: \(move)") scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: shouldOffsetY) }) { (animated:Bool) -> Void in } } // Getting next lastView & superScrollView. lastView = scrollView superScrollView = nextScrollView } else { break } } //Updating contentInset if let lastScrollViewRect = lastScrollView.superview?.convert(lastScrollView.frame, to: window) { let bottom : CGFloat = kbSize.height-newKeyboardDistanceFromTextField-(window.frame.height-lastScrollViewRect.maxY) // Update the insets so that the scroll vew doesn't shift incorrectly when the offset is near the bottom of the scroll view. var movedInsets = lastScrollView.contentInset movedInsets.bottom = max(_startingContentInsets.bottom, bottom) showLog("\(lastScrollView._IQDescription()) old ContentInset : \(lastScrollView.contentInset)") //Getting problem while using `setContentOffset:animated:`, So I used animation API. UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in lastScrollView.contentInset = movedInsets var newInset = lastScrollView.scrollIndicatorInsets newInset.bottom = movedInsets.bottom lastScrollView.scrollIndicatorInsets = newInset }) { (animated:Bool) -> Void in } showLog("\(lastScrollView._IQDescription()) new ContentInset : \(lastScrollView.contentInset)") } } //Going ahead. No else if. if layoutGuidePosition == .top { if let constraint = _layoutGuideConstraint { let constant = min(_layoutGuideConstraintInitialConstant, constraint.constant-move) UIView.animate(withDuration: _animationDuration, delay: 0, options: (_animationCurve.union(UIViewAnimationOptions.beginFromCurrentState)), animations: { () -> Void in constraint.constant = constant self._rootViewController?.view.setNeedsLayout() self._rootViewController?.view.layoutIfNeeded() }, completion: { (finished) -> Void in }) } } else if layoutGuidePosition == .bottom { if let constraint = _layoutGuideConstraint { let constant = max(_layoutGuideConstraintInitialConstant, constraint.constant+move) UIView.animate(withDuration: _animationDuration, delay: 0, options: (_animationCurve.union(UIViewAnimationOptions.beginFromCurrentState)), animations: { () -> Void in constraint.constant = constant self._rootViewController?.view.setNeedsLayout() self._rootViewController?.view.layoutIfNeeded() }, completion: { (finished) -> Void in }) } } else { //Special case for UITextView(Readjusting textView.contentInset when textView hight is too big to fit on screen) //_lastScrollView If not having inside any scrollView, (now contentInset manages the full screen textView. //[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type if let textView = textFieldView as? UITextView { let textViewHeight = min(textView.frame.height, (window.frame.height-kbSize.height-(topLayoutGuide))) if (textView.frame.size.height-textView.contentInset.bottom>textViewHeight) { UIView.animate(withDuration: _animationDuration, delay: 0, options: (_animationCurve.union(UIViewAnimationOptions.beginFromCurrentState)), animations: { () -> Void in self.showLog("\(textFieldView._IQDescription()) Old UITextView.contentInset : \(textView.contentInset)") //_isTextViewContentInsetChanged, If frame is not change by library in past, then saving user textView properties (Bug ID: #92) if (self.isTextViewContentInsetChanged == false) { self.startingTextViewContentInsets = textView.contentInset self.startingTextViewScrollIndicatorInsets = textView.scrollIndicatorInsets } var newContentInset = textView.contentInset newContentInset.bottom = textView.frame.size.height-textViewHeight textView.contentInset = newContentInset textView.scrollIndicatorInsets = newContentInset self.isTextViewContentInsetChanged = true self.showLog("\(textFieldView._IQDescription()) Old UITextView.contentInset : \(textView.contentInset)") }, completion: { (finished) -> Void in }) } } // Special case for iPad modalPresentationStyle. if rootController.modalPresentationStyle == UIModalPresentationStyle.formSheet || rootController.modalPresentationStyle == UIModalPresentationStyle.pageSheet { showLog("Found Special case for Model Presentation Style: \(rootController.modalPresentationStyle)") // +Positive or zero. if move >= 0 { // We should only manipulate y. rootViewRect.origin.y -= move // From now prevent keyboard manager to slide up the rootView to more than keyboard height. (Bug ID: #93) if preventShowingBottomBlankSpace == true { let minimumY: CGFloat = (window.frame.height-rootViewRect.size.height-topLayoutGuide)/2-(kbSize.height-newKeyboardDistanceFromTextField) rootViewRect.origin.y = max(rootViewRect.minY, minimumY) } showLog("Moving Upward") // Setting adjusted rootViewRect setRootViewFrame(rootViewRect) _privateMovedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y) } else { // -Negative // Calculating disturbed distance. Pull Request #3 let disturbDistance = rootViewRect.minY-_topViewBeginRect.minY // disturbDistance Negative = frame disturbed. // disturbDistance positive = frame not disturbed. if disturbDistance <= 0 { // We should only manipulate y. rootViewRect.origin.y -= max(move, disturbDistance) showLog("Moving Downward") // Setting adjusted rootViewRect setRootViewFrame(rootViewRect) _privateMovedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y) } } } else { //If presentation style is neither UIModalPresentationFormSheet nor UIModalPresentationPageSheet then going ahead.(General case) // +Positive or zero. if move >= 0 { rootViewRect.origin.y -= move // From now prevent keyboard manager to slide up the rootView to more than keyboard height. (Bug ID: #93) if preventShowingBottomBlankSpace == true { rootViewRect.origin.y = max(rootViewRect.origin.y, min(0, -kbSize.height+newKeyboardDistanceFromTextField)) } showLog("Moving Upward") // Setting adjusted rootViewRect setRootViewFrame(rootViewRect) _privateMovedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y) } else { // -Negative let disturbDistance : CGFloat = rootViewRect.minY-_topViewBeginRect.minY // disturbDistance Negative = frame disturbed. // disturbDistance positive = frame not disturbed. if disturbDistance <= 0 { rootViewRect.origin.y -= max(move, disturbDistance) showLog("Moving Downward") // Setting adjusted rootViewRect // Setting adjusted rootViewRect setRootViewFrame(rootViewRect) _privateMovedDistance = (_topViewBeginRect.origin.y-rootViewRect.origin.y) } } } } let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } ///--------------------- /// MARK: Public Methods ///--------------------- /* Refreshes textField/textView position if any external changes is explicitly made by user. */ open func reloadLayoutIfNeeded() -> Void { if privateIsEnabled() == true { if _textFieldView != nil && _privateIsKeyboardShowing == true && _topViewBeginRect.equalTo(CGRect.zero) == false && _textFieldView?.isAlertViewTextField() == false { adjustFrame() } } } ///------------------------------- /// MARK: UIKeyboard Notifications ///------------------------------- /* UIKeyboardWillShowNotification. */ @objc internal func keyboardWillShow(_ notification : Notification?) -> Void { _kbShowNotification = notification // Boolean to know keyboard is showing/hiding _privateIsKeyboardShowing = true let oldKBSize = _kbSize if let info = notification?.userInfo { // Getting keyboard animation. if let curve = info[UIKeyboardAnimationCurveUserInfoKey] as? UInt { _animationCurve = UIViewAnimationOptions(rawValue: curve) } else { _animationCurve = UIViewAnimationOptions.curveEaseOut } // Getting keyboard animation duration if let duration = info[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval { //Saving animation duration if duration != 0.0 { _animationDuration = duration } } else { _animationDuration = 0.25 } // Getting UIKeyboardSize. if let kbFrame = info[UIKeyboardFrameEndUserInfoKey] as? CGRect { let screenSize = UIScreen.main.bounds //Calculating actual keyboard displayed size, keyboard frame may be different when hardware keyboard is attached (Bug ID: #469) (Bug ID: #381) let intersectRect = kbFrame.intersection(screenSize) if intersectRect.isNull { _kbSize = CGSize(width: screenSize.size.width, height: 0) } else { _kbSize = intersectRect.size } showLog("UIKeyboard Size : \(_kbSize)") } } if privateIsEnabled() == false { return } let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") // (Bug ID: #5) if _textFieldView != nil && _topViewBeginRect.equalTo(CGRect.zero) == true { // keyboard is not showing(At the beginning only). We should save rootViewRect. if let constraint = _textFieldView?.viewController()?.IQLayoutGuideConstraint { _layoutGuideConstraint = constraint _layoutGuideConstraintInitialConstant = constraint.constant } // keyboard is not showing(At the beginning only). We should save rootViewRect. _rootViewController = _textFieldView?.topMostController() if _rootViewController == nil { _rootViewController = keyWindow()?.topMostWindowController() } if let unwrappedRootController = _rootViewController { _topViewBeginRect = unwrappedRootController.view.frame #if swift(>=3.2) if #available(iOS 11, *) { _initialAdditionalSafeAreaInsets = unwrappedRootController.additionalSafeAreaInsets; } #endif if _topViewBeginRect.origin.y != 0 && shouldFixInteractivePopGestureRecognizer == true && unwrappedRootController is UINavigationController && unwrappedRootController.modalPresentationStyle != UIModalPresentationStyle.formSheet && unwrappedRootController.modalPresentationStyle != UIModalPresentationStyle.pageSheet { if let window = keyWindow() { _topViewBeginRect.origin.y = window.frame.size.height-unwrappedRootController.view.frame.size.height } else { _topViewBeginRect.origin.y = 0 } } showLog("Saving \(unwrappedRootController._IQDescription()) beginning Frame: \(_topViewBeginRect)") } else { _topViewBeginRect = CGRect.zero } } // Getting topMost ViewController. var topMostController = _textFieldView?.topMostController() if topMostController == nil { topMostController = keyWindow()?.topMostWindowController() } //If last restored keyboard size is different(any orientation accure), then refresh. otherwise not. if _kbSize.equalTo(oldKBSize) == false { //If _textFieldView is inside UITableViewController then let UITableViewController to handle it (Bug ID: #37) (Bug ID: #76) See note:- https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html If it is UIAlertView textField then do not affect anything (Bug ID: #70). if _privateIsKeyboardShowing == true && _textFieldView != nil && _textFieldView?.isAlertViewTextField() == false { // keyboard is already showing. adjust frame. adjustFrame() } } let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } /* UIKeyboardDidShowNotification. */ @objc internal func keyboardDidShow(_ notification : Notification?) -> Void { if privateIsEnabled() == false { return } let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") // Getting topMost ViewController. var topMostController = _textFieldView?.topMostController() if topMostController == nil { topMostController = keyWindow()?.topMostWindowController() } if _textFieldView != nil && (topMostController?.modalPresentationStyle == UIModalPresentationStyle.formSheet || topMostController?.modalPresentationStyle == UIModalPresentationStyle.pageSheet) && _textFieldView?.isAlertViewTextField() == false { //In case of form sheet or page sheet, we'll add adjustFrame call in main queue to perform it when UI thread will do all framing updation so adjustFrame will be executed after all internal operations. OperationQueue.main.addOperation { self.adjustFrame() } } let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } /* UIKeyboardWillHideNotification. So setting rootViewController to it's default frame. */ @objc internal func keyboardWillHide(_ notification : Notification?) -> Void { //If it's not a fake notification generated by [self setEnable:NO]. if notification != nil { _kbShowNotification = nil } // Boolean to know keyboard is showing/hiding _privateIsKeyboardShowing = false if let info = notification?.userInfo { // Getting keyboard animation duration if let duration = info[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval { if duration != 0 { // Setitng keyboard animation duration _animationDuration = duration } } } //If not enabled then do nothing. if privateIsEnabled() == false { return } let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") //Commented due to #56. Added all the conditions below to handle UIWebView's textFields. (Bug ID: #56) // We are unable to get textField object while keyboard showing on UIWebView's textField. (Bug ID: #11) // if (_textFieldView == nil) return //Restoring the contentOffset of the lastScrollView if let lastScrollView = _lastScrollView { UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in lastScrollView.contentInset = self._startingContentInsets lastScrollView.scrollIndicatorInsets = self._startingScrollIndicatorInsets if lastScrollView.shouldRestoreScrollViewContentOffset == true { lastScrollView.contentOffset = self._startingContentOffset } self.showLog("Restoring \(lastScrollView._IQDescription()) contentInset to : \(self._startingContentInsets) and contentOffset to : \(self._startingContentOffset)") // TODO: restore scrollView state // This is temporary solution. Have to implement the save and restore scrollView state var superScrollView : UIScrollView? = lastScrollView while let scrollView = superScrollView { let contentSize = CGSize(width: max(scrollView.contentSize.width, scrollView.frame.width), height: max(scrollView.contentSize.height, scrollView.frame.height)) let minimumY = contentSize.height - scrollView.frame.height if minimumY < scrollView.contentOffset.y { scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: minimumY) self.showLog("Restoring \(scrollView._IQDescription()) contentOffset to : \(self._startingContentOffset)") } superScrollView = scrollView.superviewOfClassType(UIScrollView.self) as? UIScrollView } }) { (finished) -> Void in } } // Setting rootViewController frame to it's original position. // (Bug ID: #18) if _topViewBeginRect.equalTo(CGRect.zero) == false { if let rootViewController = _rootViewController { //frame size needs to be adjusted on iOS8 due to orientation API changes. _topViewBeginRect.size = rootViewController.view.frame.size //Used UIViewAnimationOptionBeginFromCurrentState to minimize strange animations. UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in if let constraint = self._layoutGuideConstraint { constraint.constant = self._layoutGuideConstraintInitialConstant rootViewController.view.setNeedsLayout() rootViewController.view.layoutIfNeeded() } else { self.showLog("Restoring \(rootViewController._IQDescription()) frame to : \(self._topViewBeginRect)") // Setting it's new frame rootViewController.view.frame = self._topViewBeginRect #if swift(>=3.2) if #available(iOS 11, *) { rootViewController.additionalSafeAreaInsets = self._initialAdditionalSafeAreaInsets; } #endif self._privateMovedDistance = 0 //Animating content if needed (Bug ID: #204) if self.layoutIfNeededOnUpdate == true { //Animating content (Bug ID: #160) rootViewController.view.setNeedsLayout() rootViewController.view.layoutIfNeeded() } } }) { (finished) -> Void in } _rootViewController = nil } } else if let constraint = self._layoutGuideConstraint { if let rootViewController = _rootViewController { UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in constraint.constant = self._layoutGuideConstraintInitialConstant rootViewController.view.setNeedsLayout() rootViewController.view.layoutIfNeeded() }) { (finished) -> Void in } } } //Reset all values _lastScrollView = nil _kbSize = CGSize.zero _layoutGuideConstraint = nil _layoutGuideConstraintInitialConstant = 0 _startingContentInsets = UIEdgeInsets.zero _startingScrollIndicatorInsets = UIEdgeInsets.zero _startingContentOffset = CGPoint.zero // topViewBeginRect = CGRectZero //Commented due to #82 let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } @objc internal func keyboardDidHide(_ notification:Notification) { let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") _topViewBeginRect = CGRect.zero #if swift(>=3.2) if #available(iOS 11, *) { _initialAdditionalSafeAreaInsets = .zero; } #endif _kbSize = CGSize.zero let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } ///------------------------------------------- /// MARK: UITextField/UITextView Notifications ///------------------------------------------- /** UITextFieldTextDidBeginEditingNotification, UITextViewTextDidBeginEditingNotification. Fetching UITextFieldView object. */ @objc internal func textFieldViewDidBeginEditing(_ notification:Notification) { let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") // Getting object _textFieldView = notification.object as? UIView if overrideKeyboardAppearance == true { if let textFieldView = _textFieldView as? UITextField { //If keyboard appearance is not like the provided appearance if textFieldView.keyboardAppearance != keyboardAppearance { //Setting textField keyboard appearance and reloading inputViews. textFieldView.keyboardAppearance = keyboardAppearance textFieldView.reloadInputViews() } } else if let textFieldView = _textFieldView as? UITextView { //If keyboard appearance is not like the provided appearance if textFieldView.keyboardAppearance != keyboardAppearance { //Setting textField keyboard appearance and reloading inputViews. textFieldView.keyboardAppearance = keyboardAppearance textFieldView.reloadInputViews() } } } //If autoToolbar enable, then add toolbar on all the UITextField/UITextView's if required. if privateIsEnableAutoToolbar() == true { //UITextView special case. Keyboard Notification is firing before textView notification so we need to resign it first and then again set it as first responder to add toolbar on it. if _textFieldView is UITextView == true && _textFieldView?.inputAccessoryView == nil { UIView.animate(withDuration: 0.00001, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in self.addToolbarIfRequired() }, completion: { (finished) -> Void in //On textView toolbar didn't appear on first time, so forcing textView to reload it's inputViews. self._textFieldView?.reloadInputViews() }) } else { //Adding toolbar addToolbarIfRequired() } } else { removeToolbarIfRequired() } resignFirstResponderGesture.isEnabled = privateShouldResignOnTouchOutside() _textFieldView?.window?.addGestureRecognizer(resignFirstResponderGesture) // (Enhancement ID: #14) if privateIsEnabled() == true { if _topViewBeginRect.equalTo(CGRect.zero) == true { // (Bug ID: #5) // keyboard is not showing(At the beginning only). We should save rootViewRect. if let constraint = _textFieldView?.viewController()?.IQLayoutGuideConstraint { _layoutGuideConstraint = constraint _layoutGuideConstraintInitialConstant = constraint.constant } _rootViewController = _textFieldView?.topMostController() if _rootViewController == nil { _rootViewController = keyWindow()?.topMostWindowController() } if let rootViewController = _rootViewController { _topViewBeginRect = rootViewController.view.frame #if swift(>=3.2) if #available(iOS 11, *) { _initialAdditionalSafeAreaInsets = rootViewController.additionalSafeAreaInsets; } #endif if _topViewBeginRect.origin.y != 0 && shouldFixInteractivePopGestureRecognizer == true && rootViewController is UINavigationController && rootViewController.modalPresentationStyle != UIModalPresentationStyle.formSheet && rootViewController.modalPresentationStyle != UIModalPresentationStyle.pageSheet { if let window = keyWindow() { _topViewBeginRect.origin.y = window.frame.size.height-rootViewController.view.frame.size.height } else { _topViewBeginRect.origin.y = 0 } } showLog("Saving \(rootViewController._IQDescription()) beginning frame : \(_topViewBeginRect)") } } //If _textFieldView is inside ignored responder then do nothing. (Bug ID: #37, #74, #76) //See notes:- https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html If it is UIAlertView textField then do not affect anything (Bug ID: #70). if _privateIsKeyboardShowing == true && _textFieldView != nil && _textFieldView?.isAlertViewTextField() == false { // keyboard is already showing. adjust frame. adjustFrame() } } let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } /** UITextFieldTextDidEndEditingNotification, UITextViewTextDidEndEditingNotification. Removing fetched object. */ @objc internal func textFieldViewDidEndEditing(_ notification:Notification) { let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") //Removing gesture recognizer (Enhancement ID: #14) _textFieldView?.window?.removeGestureRecognizer(resignFirstResponderGesture) // We check if there's a change in original frame or not. if let textView = _textFieldView as? UITextView { if isTextViewContentInsetChanged == true { UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in self.isTextViewContentInsetChanged = false self.showLog("Restoring \(textView._IQDescription()) textView.contentInset to : \(self.startingTextViewContentInsets)") //Setting textField to it's initial contentInset textView.contentInset = self.startingTextViewContentInsets textView.scrollIndicatorInsets = self.startingTextViewScrollIndicatorInsets }, completion: { (finished) -> Void in }) } } //Setting object to nil _textFieldView = nil let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } ///------------------------------------------ /// MARK: UIStatusBar Notification methods ///------------------------------------------ /** UIApplicationWillChangeStatusBarOrientationNotification. Need to set the textView to it's original position. If any frame changes made. (Bug ID: #92)*/ @objc internal func willChangeStatusBarOrientation(_ notification:Notification) { let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") //If textViewContentInsetChanged is saved then restore it. if let textView = _textFieldView as? UITextView { if isTextViewContentInsetChanged == true { UIView.animate(withDuration: _animationDuration, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState.union(_animationCurve), animations: { () -> Void in self.isTextViewContentInsetChanged = false self.showLog("Restoring \(textView._IQDescription()) textView.contentInset to : \(self.startingTextViewContentInsets)") //Setting textField to it's initial contentInset textView.contentInset = self.startingTextViewContentInsets textView.scrollIndicatorInsets = self.startingTextViewScrollIndicatorInsets }, completion: { (finished) -> Void in }) } } if privateIsEnabled() == false { return } #if swift(>=3.2) if let rootViewController = _rootViewController { if #available(iOS 11, *) { if UIEdgeInsetsEqualToEdgeInsets(_initialAdditionalSafeAreaInsets, rootViewController.additionalSafeAreaInsets) { rootViewController.additionalSafeAreaInsets = _initialAdditionalSafeAreaInsets; } } } #endif let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } /** UIApplicationDidChangeStatusBarFrameNotification. Need to refresh view position and update _topViewBeginRect. (Bug ID: #446)*/ @objc internal func didChangeStatusBarFrame(_ notification : Notification) -> Void { let oldStatusBarFrame = _statusBarFrame // Getting keyboard animation duration if let info = notification.userInfo { if let newFrame = info[UIApplicationStatusBarFrameUserInfoKey] as? CGRect { _statusBarFrame = newFrame } } if privateIsEnabled() == false { return } let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") if _rootViewController != nil && !_topViewBeginRect.equalTo(_rootViewController!.view.frame) == true { if let unwrappedRootController = _rootViewController { _topViewBeginRect = unwrappedRootController.view.frame #if swift(>=3.2) if #available(iOS 11, *) { _initialAdditionalSafeAreaInsets = unwrappedRootController.additionalSafeAreaInsets; } #endif if _topViewBeginRect.origin.y != 0 && shouldFixInteractivePopGestureRecognizer == true && unwrappedRootController is UINavigationController && unwrappedRootController.modalPresentationStyle != UIModalPresentationStyle.formSheet && unwrappedRootController.modalPresentationStyle != UIModalPresentationStyle.pageSheet { if let window = keyWindow() { _topViewBeginRect.origin.y = window.frame.size.height-unwrappedRootController.view.frame.size.height } else { _topViewBeginRect.origin.y = 0 } } showLog("Saving \(unwrappedRootController._IQDescription()) beginning Frame: \(_topViewBeginRect)") } else { _topViewBeginRect = CGRect.zero } } //If _textFieldView is inside UITableViewController then let UITableViewController to handle it (Bug ID: #37) (Bug ID: #76) See note:- https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html If it is UIAlertView textField then do not affect anything (Bug ID: #70). if _privateIsKeyboardShowing == true && _textFieldView != nil && _statusBarFrame.size.equalTo(oldStatusBarFrame.size) == false && _textFieldView?.isAlertViewTextField() == false { // keyboard is already showing. adjust frame. adjustFrame() } let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } ///------------------ /// MARK: AutoToolbar ///------------------ /** Get all UITextField/UITextView siblings of textFieldView. */ fileprivate func responderViews()-> [UIView]? { var superConsideredView : UIView? //If find any consider responderView in it's upper hierarchy then will get deepResponderView. for disabledClass in toolbarPreviousNextAllowedClasses { superConsideredView = _textFieldView?.superviewOfClassType(disabledClass) if superConsideredView != nil { break } } //If there is a superConsideredView in view's hierarchy, then fetching all it's subview that responds. No sorting for superConsideredView, it's by subView position. (Enhancement ID: #22) if superConsideredView != nil { return superConsideredView?.deepResponderViews() } else { //Otherwise fetching all the siblings if let textFields = _textFieldView?.responderSiblings() { //Sorting textFields according to behaviour switch toolbarManageBehaviour { //If autoToolbar behaviour is bySubviews, then returning it. case IQAutoToolbarManageBehaviour.bySubviews: return textFields //If autoToolbar behaviour is by tag, then sorting it according to tag property. case IQAutoToolbarManageBehaviour.byTag: return textFields.sortedArrayByTag() //If autoToolbar behaviour is by tag, then sorting it according to tag property. case IQAutoToolbarManageBehaviour.byPosition: return textFields.sortedArrayByPosition() } } else { return nil } } } /** Add toolbar if it is required to add on textFields and it's siblings. */ fileprivate func addToolbarIfRequired() { let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") // Getting all the sibling textFields. if let siblings = responderViews(), !siblings.isEmpty { showLog("Found \(siblings.count) responder sibling(s)") if let textField = _textFieldView { //Either there is no inputAccessoryView or if accessoryView is not appropriate for current situation(There is Previous/Next/Done toolbar). //setInputAccessoryView: check (Bug ID: #307) if textField.responds(to: #selector(setter: UITextField.inputAccessoryView)) { if textField.inputAccessoryView == nil || textField.inputAccessoryView?.tag == IQKeyboardManager.kIQPreviousNextButtonToolbarTag || textField.inputAccessoryView?.tag == IQKeyboardManager.kIQDoneButtonToolbarTag { // If only one object is found, then adding only Done button. if (siblings.count == 1 && previousNextDisplayMode == .Default) || previousNextDisplayMode == .alwaysHide { if let doneBarButtonItemImage = toolbarDoneBarButtonItemImage { textField.addRightButtonOnKeyboardWithImage(doneBarButtonItemImage, target: self, action: #selector(self.doneAction(_:)), shouldShowPlaceholder: shouldShowToolbarPlaceholder) } //Supporting Custom Done button text (Enhancement ID: #209, #411, Bug ID: #376) else if let doneBarButtonItemText = toolbarDoneBarButtonItemText { textField.addRightButtonOnKeyboardWithText(doneBarButtonItemText, target: self, action: #selector(self.doneAction(_:)), shouldShowPlaceholder: shouldShowToolbarPlaceholder) } else { //Now adding textField placeholder text as title of IQToolbar (Enhancement ID: #27) textField.addDoneOnKeyboardWithTarget(self, action: #selector(self.doneAction(_:)), shouldShowPlaceholder: shouldShowToolbarPlaceholder) } textField.inputAccessoryView?.tag = IQKeyboardManager.kIQDoneButtonToolbarTag // (Bug ID: #78) } else if (siblings.count > 1 && previousNextDisplayMode == .Default) || previousNextDisplayMode == .alwaysShow { //Supporting Custom Done button image (Enhancement ID: #366) if let doneBarButtonItemImage = toolbarDoneBarButtonItemImage { textField.addPreviousNextRightOnKeyboardWithTarget(self, rightButtonImage: doneBarButtonItemImage, previousAction: #selector(self.previousAction(_:)), nextAction: #selector(self.nextAction(_:)), rightButtonAction: #selector(self.doneAction(_:)), shouldShowPlaceholder: shouldShowToolbarPlaceholder) } //Supporting Custom Done button text (Enhancement ID: #209, #411, Bug ID: #376) else if let doneBarButtonItemText = toolbarDoneBarButtonItemText { textField.addPreviousNextRightOnKeyboardWithTarget(self, rightButtonTitle: doneBarButtonItemText, previousAction: #selector(self.previousAction(_:)), nextAction: #selector(self.nextAction(_:)), rightButtonAction: #selector(self.doneAction(_:)), shouldShowPlaceholder: shouldShowToolbarPlaceholder) } else { //Now adding textField placeholder text as title of IQToolbar (Enhancement ID: #27) textField.addPreviousNextDoneOnKeyboardWithTarget(self, previousAction: #selector(self.previousAction(_:)), nextAction: #selector(self.nextAction(_:)), doneAction: #selector(self.doneAction(_:)), shouldShowPlaceholder: shouldShowToolbarPlaceholder) } textField.inputAccessoryView?.tag = IQKeyboardManager.kIQPreviousNextButtonToolbarTag // (Bug ID: #78) } let toolbar = textField.keyboardToolbar // Setting toolbar to keyboard. if let _textField = textField as? UITextField { //Bar style according to keyboard appearance switch _textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black toolbar.tintColor = UIColor.white toolbar.barTintColor = nil default: toolbar.barStyle = UIBarStyle.default toolbar.barTintColor = toolbarBarTintColor //Setting toolbar tintColor // (Enhancement ID: #30) if shouldToolbarUsesTextFieldTintColor { toolbar.tintColor = _textField.tintColor } else if let tintColor = toolbarTintColor { toolbar.tintColor = tintColor } else { toolbar.tintColor = UIColor.black } } } else if let _textView = textField as? UITextView { //Bar style according to keyboard appearance switch _textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black toolbar.tintColor = UIColor.white toolbar.barTintColor = nil default: toolbar.barStyle = UIBarStyle.default toolbar.barTintColor = toolbarBarTintColor if shouldToolbarUsesTextFieldTintColor { toolbar.tintColor = _textView.tintColor } else if let tintColor = toolbarTintColor { toolbar.tintColor = tintColor } else { toolbar.tintColor = UIColor.black } } } //Setting toolbar title font. // (Enhancement ID: #30) if shouldShowToolbarPlaceholder == true && textField.shouldHideToolbarPlaceholder == false { //Updating placeholder font to toolbar. //(Bug ID: #148, #272) if toolbar.titleBarButton.title == nil || toolbar.titleBarButton.title != textField.drawingToolbarPlaceholder { toolbar.titleBarButton.title = textField.drawingToolbarPlaceholder } //Setting toolbar title font. // (Enhancement ID: #30) if placeholderFont != nil { toolbar.titleBarButton.titleFont = placeholderFont } } else { toolbar.titleBarButton.title = nil } //In case of UITableView (Special), the next/previous buttons has to be refreshed everytime. (Bug ID: #56) // If firstTextField, then previous should not be enabled. if siblings.first == textField { if (siblings.count == 1) { textField.keyboardToolbar.previousBarButton.isEnabled = false textField.keyboardToolbar.nextBarButton.isEnabled = false } else { textField.keyboardToolbar.previousBarButton.isEnabled = false textField.keyboardToolbar.nextBarButton.isEnabled = true } } else if siblings.last == textField { // If lastTextField then next should not be enaled. textField.keyboardToolbar.previousBarButton.isEnabled = true textField.keyboardToolbar.nextBarButton.isEnabled = false } else { textField.keyboardToolbar.previousBarButton.isEnabled = true textField.keyboardToolbar.nextBarButton.isEnabled = true } } } } } let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } /** Remove any toolbar if it is IQToolbar. */ fileprivate func removeToolbarIfRequired() { // (Bug ID: #18) let startTime = CACurrentMediaTime() showLog("****** \(#function) started ******") // Getting all the sibling textFields. if let siblings = responderViews() { showLog("Found \(siblings.count) responder sibling(s)") for view in siblings { if let toolbar = view.inputAccessoryView as? IQToolbar { //setInputAccessoryView: check (Bug ID: #307) if view.responds(to: #selector(setter: UITextField.inputAccessoryView)) && (toolbar.tag == IQKeyboardManager.kIQDoneButtonToolbarTag || toolbar.tag == IQKeyboardManager.kIQPreviousNextButtonToolbarTag) { if let textField = view as? UITextField { textField.inputAccessoryView = nil textField.reloadInputViews() } else if let textView = view as? UITextView { textView.inputAccessoryView = nil textView.reloadInputViews() } } } } } let elapsedTime = CACurrentMediaTime() - startTime showLog("****** \(#function) ended: \(elapsedTime) seconds ******") } /** reloadInputViews to reload toolbar buttons enable/disable state on the fly Enhancement ID #434. */ open func reloadInputViews() { //If enabled then adding toolbar. if privateIsEnableAutoToolbar() == true { self.addToolbarIfRequired() } else { self.removeToolbarIfRequired() } } ///------------------ /// MARK: Debugging & Developer options ///------------------ open var enableDebugging = false /** @warning Use below methods to completely enable/disable notifications registered by library internally. Please keep in mind that library is totally dependent on NSNotification of UITextField, UITextField, Keyboard etc. If you do unregisterAllNotifications then library will not work at all. You should only use below methods if you want to completedly disable all library functions. You should use below methods at your own risk. */ open func registerAllNotifications() { // Registering for keyboard notification. NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: Notification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow(_:)), name: Notification.Name.UIKeyboardDidShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: Notification.Name.UIKeyboardWillHide, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(_:)), name: Notification.Name.UIKeyboardDidHide, object: nil) // Registering for UITextField notification. registerTextFieldViewClass(UITextField.self, didBeginEditingNotificationName: Notification.Name.UITextFieldTextDidBeginEditing.rawValue, didEndEditingNotificationName: Notification.Name.UITextFieldTextDidEndEditing.rawValue) // Registering for UITextView notification. registerTextFieldViewClass(UITextView.self, didBeginEditingNotificationName: Notification.Name.UITextViewTextDidBeginEditing.rawValue, didEndEditingNotificationName: Notification.Name.UITextViewTextDidEndEditing.rawValue) // Registering for orientation changes notification NotificationCenter.default.addObserver(self, selector: #selector(self.willChangeStatusBarOrientation(_:)), name: Notification.Name.UIApplicationWillChangeStatusBarOrientation, object: UIApplication.shared) // Registering for status bar frame change notification NotificationCenter.default.addObserver(self, selector: #selector(self.didChangeStatusBarFrame(_:)), name: Notification.Name.UIApplicationDidChangeStatusBarFrame, object: UIApplication.shared) } open func unregisterAllNotifications() { // Unregistering for keyboard notification. NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardDidShow, object: nil) NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillHide, object: nil) NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardDidHide, object: nil) // Unregistering for UITextField notification. unregisterTextFieldViewClass(UITextField.self, didBeginEditingNotificationName: Notification.Name.UITextFieldTextDidBeginEditing.rawValue, didEndEditingNotificationName: Notification.Name.UITextFieldTextDidEndEditing.rawValue) // Unregistering for UITextView notification. unregisterTextFieldViewClass(UITextView.self, didBeginEditingNotificationName: Notification.Name.UITextViewTextDidBeginEditing.rawValue, didEndEditingNotificationName: Notification.Name.UITextViewTextDidEndEditing.rawValue) // Unregistering for orientation changes notification NotificationCenter.default.removeObserver(self, name: Notification.Name.UIApplicationWillChangeStatusBarOrientation, object: UIApplication.shared) // Unregistering for status bar frame change notification NotificationCenter.default.removeObserver(self, name: Notification.Name.UIApplicationDidChangeStatusBarFrame, object: UIApplication.shared) } fileprivate func showLog(_ logString: String) { if enableDebugging { print("IQKeyboardManager: " + logString) } } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift ================================================ // // IQKeyboardReturnKeyHandler.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation import UIKit /** Manages the return key to work like next/done in a view hierarchy. */ open class IQKeyboardReturnKeyHandler: NSObject , UITextFieldDelegate, UITextViewDelegate { ///--------------- /// MARK: Settings ///--------------- /** Delegate of textField/textView. */ open weak var delegate: (UITextFieldDelegate & UITextViewDelegate)? /** Set the last textfield return key type. Default is UIReturnKeyDefault. */ open var lastTextFieldReturnKeyType : UIReturnKeyType = UIReturnKeyType.default { didSet { for infoDict in textFieldInfoCache { if let view = infoDict[kIQTextField] as? UIView { updateReturnKeyTypeOnTextField(view) } } } } ///-------------------------------------- /// MARK: Initialization/Deinitialization ///-------------------------------------- public override init() { super.init() } /** Add all the textFields available in UIViewController's view. */ public init(controller : UIViewController) { super.init() addResponderFromView(controller.view) } deinit { for infoDict in textFieldInfoCache { if let textField = infoDict[kIQTextField] as? UITextField { if let returnKeyType = infoDict[kIQTextFieldReturnKeyType] as? UIReturnKeyType { textField.returnKeyType = returnKeyType } textField.delegate = infoDict[kIQTextFieldDelegate] as? UITextFieldDelegate } else if let textView = infoDict[kIQTextField] as? UITextView { if let returnKeyType = infoDict[kIQTextFieldReturnKeyType] as? UIReturnKeyType { textView.returnKeyType = returnKeyType } textView.delegate = infoDict[kIQTextFieldDelegate] as? UITextViewDelegate } } textFieldInfoCache.removeAll() } ///------------------------ /// MARK: Private variables ///------------------------ fileprivate var textFieldInfoCache = [[AnyHashable : Any]]() fileprivate let kIQTextField = "kIQTextField" fileprivate let kIQTextFieldDelegate = "kIQTextFieldDelegate" fileprivate let kIQTextFieldReturnKeyType = "kIQTextFieldReturnKeyType" ///------------------------ /// MARK: Private Functions ///------------------------ fileprivate func textFieldViewCachedInfo(_ textField : UIView) -> [AnyHashable : Any]? { for infoDict in textFieldInfoCache { if let view = infoDict[kIQTextField] as? UIView { if view == textField { return infoDict } } } return nil } fileprivate func updateReturnKeyTypeOnTextField(_ view : UIView) { var superConsideredView : UIView? //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347) for disabledClass in IQKeyboardManager.sharedManager().toolbarPreviousNextAllowedClasses { superConsideredView = view.superviewOfClassType(disabledClass) if superConsideredView != nil { break } } var textFields : [UIView]? //If there is a tableView in view's hierarchy, then fetching all it's subview that responds. if let unwrappedTableView = superConsideredView { // (Enhancement ID: #22) textFields = unwrappedTableView.deepResponderViews() } else { //Otherwise fetching all the siblings textFields = view.responderSiblings() //Sorting textFields according to behaviour switch IQKeyboardManager.sharedManager().toolbarManageBehaviour { //If needs to sort it by tag case .byTag: textFields = textFields?.sortedArrayByTag() //If needs to sort it by Position case .byPosition: textFields = textFields?.sortedArrayByPosition() default: break } } if let lastView = textFields?.last { if let textField = view as? UITextField { //If it's the last textField in responder view, else next textField.returnKeyType = (view == lastView) ? lastTextFieldReturnKeyType : UIReturnKeyType.next } else if let textView = view as? UITextView { //If it's the last textField in responder view, else next textView.returnKeyType = (view == lastView) ? lastTextFieldReturnKeyType : UIReturnKeyType.next } } } ///---------------------------------------------- /// MARK: Registering/Unregistering textFieldView ///---------------------------------------------- /** Should pass UITextField/UITextView intance. Assign textFieldView delegate to self, change it's returnKeyType. @param textFieldView UITextField/UITextView object to register. */ open func addTextFieldView(_ view : UIView) { var dictInfo = [AnyHashable : Any]() dictInfo[kIQTextField] = view if let textField = view as? UITextField { dictInfo[kIQTextFieldReturnKeyType] = textField.returnKeyType if let textFieldDelegate = textField.delegate { dictInfo[kIQTextFieldDelegate] = textFieldDelegate } textField.delegate = self } else if let textView = view as? UITextView { dictInfo[kIQTextFieldReturnKeyType] = textView.returnKeyType if let textViewDelegate = textView.delegate { dictInfo[kIQTextFieldDelegate] = textViewDelegate } textView.delegate = self } textFieldInfoCache.append(dictInfo) } /** Should pass UITextField/UITextView intance. Restore it's textFieldView delegate and it's returnKeyType. @param textFieldView UITextField/UITextView object to unregister. */ open func removeTextFieldView(_ view : UIView) { if let infoDict = textFieldViewCachedInfo(view) { if let textField = view as? UITextField { if let returnKeyType = infoDict[kIQTextFieldReturnKeyType] as? UIReturnKeyType { textField.returnKeyType = returnKeyType } textField.delegate = infoDict[kIQTextFieldDelegate] as? UITextFieldDelegate } else if let textView = view as? UITextView { if let returnKeyType = infoDict[kIQTextFieldReturnKeyType] as? UIReturnKeyType { textView.returnKeyType = returnKeyType } textView.delegate = infoDict[kIQTextFieldDelegate] as? UITextViewDelegate } if let index = textFieldInfoCache.index(where: { $0[kIQTextField] as! UIView == view}) { textFieldInfoCache.remove(at: index) } } } /** Add all the UITextField/UITextView responderView's. @param UIView object to register all it's responder subviews. */ open func addResponderFromView(_ view : UIView) { let textFields = view.deepResponderViews() for textField in textFields { addTextFieldView(textField) } } /** Remove all the UITextField/UITextView responderView's. @param UIView object to unregister all it's responder subviews. */ open func removeResponderFromView(_ view : UIView) { let textFields = view.deepResponderViews() for textField in textFields { removeTextFieldView(textField) } } @discardableResult fileprivate func goToNextResponderOrResign(_ view : UIView) -> Bool { var superConsideredView : UIView? //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347) for disabledClass in IQKeyboardManager.sharedManager().toolbarPreviousNextAllowedClasses { superConsideredView = view.superviewOfClassType(disabledClass) if superConsideredView != nil { break } } var textFields : [UIView]? //If there is a tableView in view's hierarchy, then fetching all it's subview that responds. if let unwrappedTableView = superConsideredView { // (Enhancement ID: #22) textFields = unwrappedTableView.deepResponderViews() } else { //Otherwise fetching all the siblings textFields = view.responderSiblings() //Sorting textFields according to behaviour switch IQKeyboardManager.sharedManager().toolbarManageBehaviour { //If needs to sort it by tag case .byTag: textFields = textFields?.sortedArrayByTag() //If needs to sort it by Position case .byPosition: textFields = textFields?.sortedArrayByPosition() default: break } } if let unwrappedTextFields = textFields { //Getting index of current textField. if let index = unwrappedTextFields.index(of: view) { //If it is not last textField. then it's next object becomeFirstResponder. if index < (unwrappedTextFields.count - 1) { let nextTextField = unwrappedTextFields[index+1] nextTextField.becomeFirstResponder() return false } else { view.resignFirstResponder() return true } } else { return true } } else { return true } } ///---------------------------------------------- /// MARK: UITextField/UITextView delegates ///---------------------------------------------- open func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textField)?[kIQTextFieldDelegate] as? UITextFieldDelegate { if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldBeginEditing(_:))) { return unwrapDelegate.textFieldShouldBeginEditing?(textField) == true } } } return true } open func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textField)?[kIQTextFieldDelegate] as? UITextFieldDelegate { if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:))) { return unwrapDelegate.textFieldShouldEndEditing?(textField) == true } } } return true } open func textFieldDidBeginEditing(_ textField: UITextField) { updateReturnKeyTypeOnTextField(textField) var aDelegate : UITextFieldDelegate? = delegate if aDelegate == nil { if let dict = textFieldViewCachedInfo(textField) { aDelegate = dict[kIQTextFieldDelegate] as? UITextFieldDelegate } } aDelegate?.textFieldDidBeginEditing?(textField) } open func textFieldDidEndEditing(_ textField: UITextField) { var aDelegate : UITextFieldDelegate? = delegate if aDelegate == nil { if let dict = textFieldViewCachedInfo(textField) { aDelegate = dict[kIQTextFieldDelegate] as? UITextFieldDelegate } } aDelegate?.textFieldDidEndEditing?(textField) } @available(iOS 10.0, *) open func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) { var aDelegate : UITextFieldDelegate? = delegate if aDelegate == nil { if let dict = textFieldViewCachedInfo(textField) { aDelegate = dict[kIQTextFieldDelegate] as? UITextFieldDelegate } } aDelegate?.textFieldDidEndEditing?(textField, reason: reason) } open func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textField)?[kIQTextFieldDelegate] as? UITextFieldDelegate { if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textField(_:shouldChangeCharactersIn:replacementString:))) { return unwrapDelegate.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == true } } } return true } open func textFieldShouldClear(_ textField: UITextField) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textField)?[kIQTextFieldDelegate] as? UITextFieldDelegate { if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldClear(_:))) { return unwrapDelegate.textFieldShouldClear?(textField) == true } } } return true } open func textFieldShouldReturn(_ textField: UITextField) -> Bool { var shouldReturn = true if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textField)?[kIQTextFieldDelegate] as? UITextFieldDelegate { if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldReturn(_:))) { shouldReturn = unwrapDelegate.textFieldShouldReturn?(textField) == true } } } if shouldReturn == true { goToNextResponderOrResign(textField) return true } else { return goToNextResponderOrResign(textField) } } open func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textView)?[kIQTextFieldDelegate] as? UITextViewDelegate { if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textViewShouldBeginEditing(_:))) { return unwrapDelegate.textViewShouldBeginEditing?(textView) == true } } } return true } open func textViewShouldEndEditing(_ textView: UITextView) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textView)?[kIQTextFieldDelegate] as? UITextViewDelegate { if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textViewShouldEndEditing(_:))) { return unwrapDelegate.textViewShouldEndEditing?(textView) == true } } } return true } open func textViewDidBeginEditing(_ textView: UITextView) { updateReturnKeyTypeOnTextField(textView) var aDelegate : UITextViewDelegate? = delegate if aDelegate == nil { if let dict = textFieldViewCachedInfo(textView) { aDelegate = dict[kIQTextFieldDelegate] as? UITextViewDelegate } } aDelegate?.textViewDidBeginEditing?(textView) } open func textViewDidEndEditing(_ textView: UITextView) { var aDelegate : UITextViewDelegate? = delegate if aDelegate == nil { if let dict = textFieldViewCachedInfo(textView) { aDelegate = dict[kIQTextFieldDelegate] as? UITextViewDelegate } } aDelegate?.textViewDidEndEditing?(textView) } open func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { var shouldReturn = true if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(textView)?[kIQTextFieldDelegate] as? UITextViewDelegate { if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textView(_:shouldChangeTextIn:replacementText:))) { shouldReturn = (unwrapDelegate.textView?(textView, shouldChangeTextIn: range, replacementText: text)) == true } } } if shouldReturn == true && text == "\n" { shouldReturn = goToNextResponderOrResign(textView) } return shouldReturn } open func textViewDidChange(_ textView: UITextView) { var aDelegate : UITextViewDelegate? = delegate if aDelegate == nil { if let dict = textFieldViewCachedInfo(textView) { aDelegate = dict[kIQTextFieldDelegate] as? UITextViewDelegate } } aDelegate?.textViewDidChange?(textView) } open func textViewDidChangeSelection(_ textView: UITextView) { var aDelegate : UITextViewDelegate? = delegate if aDelegate == nil { if let dict = textFieldViewCachedInfo(textView) { aDelegate = dict[kIQTextFieldDelegate] as? UITextViewDelegate } } aDelegate?.textViewDidChangeSelection?(textView) } @available(iOS 10.0, *) open func textView(_ aTextView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?[kIQTextFieldDelegate] as? UITextViewDelegate { if unwrapDelegate.responds(to: #selector(textView as (UITextView, URL, NSRange, UITextItemInteraction) -> Bool)) { return unwrapDelegate.textView?(aTextView, shouldInteractWith: URL, in: characterRange, interaction: interaction) == true } } } return true } @available(iOS 10.0, *) open func textView(_ aTextView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?[kIQTextFieldDelegate] as? UITextViewDelegate { if unwrapDelegate.responds(to: #selector(textView as (UITextView, NSTextAttachment, NSRange, UITextItemInteraction) -> Bool)) { return unwrapDelegate.textView?(aTextView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) == true } } } return true } @available(iOS, deprecated: 10.0) open func textView(_ aTextView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?[kIQTextFieldDelegate] as? UITextViewDelegate { if unwrapDelegate.responds(to: #selector(textView as (UITextView, URL, NSRange) -> Bool)) { return unwrapDelegate.textView?(aTextView, shouldInteractWith: URL, in: characterRange) == true } } } return true } @available(iOS, deprecated: 10.0) open func textView(_ aTextView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool { if delegate == nil { if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?[kIQTextFieldDelegate] as? UITextViewDelegate { if unwrapDelegate.responds(to: #selector(textView as (UITextView, NSTextAttachment, NSRange) -> Bool)) { return unwrapDelegate.textView?(aTextView, shouldInteractWith: textAttachment, in: characterRange) == true } } } return true } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift ================================================ // // IQTextView.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit /** @abstract UITextView with placeholder support */ open class IQTextView : UITextView { required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: Notification.Name.UITextViewTextDidChange, object: self) } override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: Notification.Name.UITextViewTextDidChange, object: self) } override open func awakeFromNib() { super.awakeFromNib() NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: Notification.Name.UITextViewTextDidChange, object: self) } deinit { NotificationCenter.default.removeObserver(self) } fileprivate var placeholderLabel: UILabel? /** @abstract To set textView's placeholder text. Default is ni. */ @IBInspectable open var placeholder : String? { get { return placeholderLabel?.text } set { if placeholderLabel == nil { placeholderLabel = UILabel() if let unwrappedPlaceholderLabel = placeholderLabel { unwrappedPlaceholderLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight] unwrappedPlaceholderLabel.lineBreakMode = .byWordWrapping unwrappedPlaceholderLabel.numberOfLines = 0 unwrappedPlaceholderLabel.font = self.font unwrappedPlaceholderLabel.textAlignment = self.textAlignment unwrappedPlaceholderLabel.backgroundColor = UIColor.clear unwrappedPlaceholderLabel.textColor = UIColor(white: 0.7, alpha: 1.0) unwrappedPlaceholderLabel.alpha = 0 addSubview(unwrappedPlaceholderLabel) } } placeholderLabel?.text = newValue refreshPlaceholder() } } open override func layoutSubviews() { super.layoutSubviews() if let unwrappedPlaceholderLabel = placeholderLabel { let offsetLeft = textContainerInset.left + textContainer.lineFragmentPadding let offsetRight = textContainerInset.right + textContainer.lineFragmentPadding let offsetTop = textContainerInset.top let offsetBottom = textContainerInset.top let expectedSize = unwrappedPlaceholderLabel.sizeThatFits(CGSize(width: self.frame.width-offsetLeft-offsetRight, height: self.frame.height-offsetTop-offsetBottom)) unwrappedPlaceholderLabel.frame = CGRect(x: offsetLeft, y: offsetTop, width: expectedSize.width, height: expectedSize.height) } } @objc open func refreshPlaceholder() { if text.characters.count != 0 { placeholderLabel?.alpha = 0 } else { placeholderLabel?.alpha = 1 } } override open var text: String! { didSet { refreshPlaceholder() } } override open var font : UIFont? { didSet { if let unwrappedFont = font { placeholderLabel?.font = unwrappedFont } else { placeholderLabel?.font = UIFont.systemFont(ofSize: 12) } } } override open var textAlignment: NSTextAlignment { didSet { placeholderLabel?.textAlignment = textAlignment } } override open var delegate : UITextViewDelegate? { get { refreshPlaceholder() return super.delegate } set { super.delegate = newValue } } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift ================================================ // // IQBarButtonItem.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit import Foundation open class IQBarButtonItem: UIBarButtonItem { private static var _classInitialize: Void = classInitialize() public override init() { _ = IQBarButtonItem._classInitialize super.init() } public required init?(coder aDecoder: NSCoder) { _ = IQBarButtonItem._classInitialize super.init(coder: aDecoder) } private class func classInitialize() { let appearanceProxy = self.appearance() let states : [UIControlState] = [.normal,.highlighted,.disabled,.selected,.application,.reserved]; for state in states { appearanceProxy.setBackgroundImage(nil, for: state, barMetrics: .default) appearanceProxy.setBackgroundImage(nil, for: state, style: .done, barMetrics: .default) appearanceProxy.setBackgroundImage(nil, for: state, style: .plain, barMetrics: .default) appearanceProxy.setBackButtonBackgroundImage(nil, for: state, barMetrics: .default) } appearanceProxy.setTitlePositionAdjustment(UIOffset.zero, for: .default) appearanceProxy.setBackgroundVerticalPositionAdjustment(0, for: .default) appearanceProxy.setBackButtonTitlePositionAdjustment(UIOffset.zero, for: .default) appearanceProxy.setBackButtonBackgroundVerticalPositionAdjustment(0, for: .default) } open override var tintColor: UIColor? { didSet { #if swift(>=4) var textAttributes = [NSAttributedStringKey : Any]() if let attributes = titleTextAttributes(for: .normal) { for (key, value) in attributes { textAttributes[NSAttributedStringKey.init(key)] = value } } textAttributes[NSAttributedStringKey.foregroundColor] = tintColor setTitleTextAttributes(textAttributes, for: .normal) #else var textAttributes = [String:Any]() if let attributes = titleTextAttributes(for: .normal) { textAttributes = attributes } textAttributes[NSForegroundColorAttributeName] = tintColor setTitleTextAttributes(textAttributes, for: .normal) #endif } } /** Boolean to know if it's a system item or custom item, we are having a limitation that we cannot override a designated initializer, so we are manually setting this property once in initialization */ @objc var isSystemItem = false // public override init(barButtonSystemItem systemItem: UIBarButtonSystemItem, target: Any?, action: Selector?) { // return super.init(barButtonSystemItem: systemItem, target: target, action: action) // } /** Additional target & action to do get callback action. Note that setting custom target & selector doesn't affect native functionality, this is just an additional target to get a callback. @param target Target object. @param action Target Selector. */ open func setTarget(_ target: AnyObject?, action: Selector?) { invocation = (target, action) } /** Customized Invocation to be called when button is pressed. invocation is internally created using setTarget:action: method. */ open var invocation : (target: AnyObject?, action: Selector?) } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift ================================================ // // IQPreviousNextView.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit open class IQPreviousNextView: UIView { } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift ================================================ // // IQTitleBarButtonItem.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import Foundation import UIKit open class IQTitleBarButtonItem: IQBarButtonItem { open var titleFont : UIFont? { didSet { if let unwrappedFont = titleFont { _titleButton?.titleLabel?.font = unwrappedFont } else { _titleButton?.titleLabel?.font = UIFont.systemFont(ofSize: 13) } } } override open var title: String? { didSet { _titleButton?.setTitle(title, for: UIControlState()) } } /** selectableTextColor to be used for displaying button text when button is enabled. */ open var selectableTextColor : UIColor? { didSet { if let color = selectableTextColor { _titleButton?.setTitleColor(color, for:UIControlState()) } else { _titleButton?.setTitleColor(UIColor.init(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for:UIControlState()) } } } /** Customized Invocation to be called on title button action. titleInvocation is internally created using setTitleTarget:action: method. */ override open var invocation : (target: AnyObject?, action: Selector?) { didSet { if (invocation.target == nil || invocation.action == nil) { self.isEnabled = false _titleButton?.isEnabled = false _titleButton?.removeTarget(nil, action: nil, for: .touchUpInside) } else { self.isEnabled = true _titleButton?.isEnabled = true _titleButton?.addTarget(invocation.target, action: invocation.action!, for: .touchUpInside) } } } fileprivate var _titleButton : UIButton? fileprivate var _titleView : UIView? override init() { super.init() } convenience init(title : String?) { self.init(title: nil, style: UIBarButtonItemStyle.plain, target: nil, action: nil) _titleView = UIView() _titleView?.backgroundColor = UIColor.clear _titleButton = UIButton(type: .system) _titleButton?.isEnabled = false _titleButton?.titleLabel?.numberOfLines = 3 _titleButton?.setTitleColor(UIColor.lightGray, for:.disabled) _titleButton?.setTitleColor(UIColor.init(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for:UIControlState()) _titleButton?.backgroundColor = UIColor.clear _titleButton?.titleLabel?.textAlignment = .center _titleButton?.setTitle(title, for: UIControlState()) titleFont = UIFont.systemFont(ofSize: 13.0) _titleButton?.titleLabel?.font = self.titleFont _titleView?.addSubview(_titleButton!) #if swift(>=3.2) if #available(iOS 11, *) { var layoutDefaultLowPriority : UILayoutPriority var layoutDefaultHighPriority : UILayoutPriority #if swift(>=4.0) let layoutPriorityLowValue = UILayoutPriority.defaultLow.rawValue-1 let layoutPriorityHighValue = UILayoutPriority.defaultHigh.rawValue-1 layoutDefaultLowPriority = UILayoutPriority(rawValue: layoutPriorityLowValue) layoutDefaultHighPriority = UILayoutPriority(rawValue: layoutPriorityHighValue) #else layoutDefaultLowPriority = UILayoutPriorityDefaultLow-1 layoutDefaultHighPriority = UILayoutPriorityDefaultHigh-1 #endif _titleView?.translatesAutoresizingMaskIntoConstraints = false _titleView?.setContentHuggingPriority(layoutDefaultLowPriority, for: .vertical) _titleView?.setContentHuggingPriority(layoutDefaultLowPriority, for: .horizontal) _titleView?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .vertical) _titleView?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .horizontal) _titleButton?.translatesAutoresizingMaskIntoConstraints = false _titleButton?.setContentHuggingPriority(layoutDefaultLowPriority, for: .vertical) _titleButton?.setContentHuggingPriority(layoutDefaultLowPriority, for: .horizontal) _titleButton?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .vertical) _titleButton?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .horizontal) let top = NSLayoutConstraint.init(item: _titleButton!, attribute: .top, relatedBy: .equal, toItem: _titleView, attribute: .top, multiplier: 1, constant: 0) let bottom = NSLayoutConstraint.init(item: _titleButton!, attribute: .bottom, relatedBy: .equal, toItem: _titleView, attribute: .bottom, multiplier: 1, constant: 0) let leading = NSLayoutConstraint.init(item: _titleButton!, attribute: .leading, relatedBy: .equal, toItem: _titleView, attribute: .leading, multiplier: 1, constant: 0) let trailing = NSLayoutConstraint.init(item: _titleButton!, attribute: .trailing, relatedBy: .equal, toItem: _titleView, attribute: .trailing, multiplier: 1, constant: 0) _titleView?.addConstraints([top,bottom,leading,trailing]) } else { _titleView?.autoresizingMask = [.flexibleWidth,.flexibleHeight] _titleButton?.autoresizingMask = [.flexibleWidth,.flexibleHeight] } #else _titleView?.autoresizingMask = [.flexibleWidth,.flexibleHeight] _titleButton?.autoresizingMask = [.flexibleWidth,.flexibleHeight] #endif customView = _titleView } required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift ================================================ // // IQToolbar.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit /** @abstract IQToolbar for IQKeyboardManager. */ open class IQToolbar: UIToolbar , UIInputViewAudioFeedback { private static var _classInitialize: Void = classInitialize() private class func classInitialize() { let appearanceProxy = self.appearance() appearanceProxy.barTintColor = nil let positions : [UIBarPosition] = [.any,.bottom,.top,.topAttached]; for position in positions { appearanceProxy.setBackgroundImage(nil, forToolbarPosition: position, barMetrics: .default) appearanceProxy.setShadowImage(nil, forToolbarPosition: .any) } //Background color appearanceProxy.backgroundColor = nil } /** Previous bar button of toolbar. */ private var privatePreviousBarButton: IQBarButtonItem? open var previousBarButton : IQBarButtonItem { get { if privatePreviousBarButton == nil { privatePreviousBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil) privatePreviousBarButton?.accessibilityLabel = "Toolbar Previous Button" } return privatePreviousBarButton! } set (newValue) { privatePreviousBarButton = newValue } } /** Next bar button of toolbar. */ private var privateNextBarButton: IQBarButtonItem? open var nextBarButton : IQBarButtonItem { get { if privateNextBarButton == nil { privateNextBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil) privateNextBarButton?.accessibilityLabel = "Toolbar Next Button" } return privateNextBarButton! } set (newValue) { privateNextBarButton = newValue } } /** Title bar button of toolbar. */ private var privateTitleBarButton: IQTitleBarButtonItem? open var titleBarButton : IQTitleBarButtonItem { get { if privateTitleBarButton == nil { privateTitleBarButton = IQTitleBarButtonItem(title: nil) privateTitleBarButton?.accessibilityLabel = "Toolbar Title Button" } return privateTitleBarButton! } set (newValue) { privateTitleBarButton = newValue } } /** Done bar button of toolbar. */ private var privateDoneBarButton: IQBarButtonItem? open var doneBarButton : IQBarButtonItem { get { if privateDoneBarButton == nil { privateDoneBarButton = IQBarButtonItem(title: nil, style: .done, target: nil, action: nil) privateDoneBarButton?.accessibilityLabel = "Toolbar Done Button" } return privateDoneBarButton! } set (newValue) { privateDoneBarButton = newValue } } override init(frame: CGRect) { _ = IQToolbar._classInitialize super.init(frame: frame) sizeToFit() autoresizingMask = UIViewAutoresizing.flexibleWidth self.isTranslucent = true } required public init?(coder aDecoder: NSCoder) { _ = IQToolbar._classInitialize super.init(coder: aDecoder) sizeToFit() autoresizingMask = UIViewAutoresizing.flexibleWidth self.isTranslucent = true } override open func sizeThatFits(_ size: CGSize) -> CGSize { var sizeThatFit = super.sizeThatFits(size) sizeThatFit.height = 44 return sizeThatFit } override open var tintColor: UIColor! { didSet { if let unwrappedItems = items { for item in unwrappedItems { item.tintColor = tintColor } } } } override open var barStyle: UIBarStyle { didSet { if barStyle == .default { titleBarButton.selectableTextColor = UIColor.init(red: 0.0, green: 0.5, blue: 1.0, alpha: 1) } else { titleBarButton.selectableTextColor = UIColor.yellow } } } override open func layoutSubviews() { super.layoutSubviews() //If running on Xcode9 (iOS11) only then we'll validate for iOS version, otherwise for older versions of Xcode (iOS10 and below) we'll just execute the tweak #if swift(>=3.2) if #available(iOS 11, *) { return } else { var leftRect = CGRect.null var rightRect = CGRect.null var isTitleBarButtonFound = false let sortedSubviews = self.subviews.sorted(by: { (view1 : UIView, view2 : UIView) -> Bool in let x1 = view1.frame.minX let y1 = view1.frame.minY let x2 = view2.frame.minX let y2 = view2.frame.minY if x1 != x2 { return x1 < x2 } else { return y1 < y2 } }) for barButtonItemView in sortedSubviews { if isTitleBarButtonFound == true { rightRect = barButtonItemView.frame break } else if type(of: barButtonItemView) === UIView.self { isTitleBarButtonFound = true //If it's UIToolbarButton or UIToolbarTextButton (which actually UIBarButtonItem) } else if barButtonItemView.isKind(of: UIControl.self) == true { leftRect = barButtonItemView.frame } } var x : CGFloat = 16 if (leftRect.isNull == false) { x = leftRect.maxX + 16 } let width : CGFloat = self.frame.width - 32 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX) if let unwrappedItems = items { for item in unwrappedItems { if let newItem = item as? IQTitleBarButtonItem { let titleRect = CGRect(x: x, y: 0, width: width, height: self.frame.size.height) newItem.customView?.frame = titleRect break } } } } #else var leftRect = CGRect.null var rightRect = CGRect.null var isTitleBarButtonFound = false let sortedSubviews = self.subviews.sorted(by: { (view1 : UIView, view2 : UIView) -> Bool in let x1 = view1.frame.minX let y1 = view1.frame.minY let x2 = view2.frame.minX let y2 = view2.frame.minY if x1 != x2 { return x1 < x2 } else { return y1 < y2 } }) for barButtonItemView in sortedSubviews { if isTitleBarButtonFound == true { rightRect = barButtonItemView.frame break } else if type(of: barButtonItemView) === UIView.self { isTitleBarButtonFound = true //If it's UIToolbarButton or UIToolbarTextButton (which actually UIBarButtonItem) } else if barButtonItemView.isKind(of: UIControl.self) == true { leftRect = barButtonItemView.frame } } var x : CGFloat = 16 if (leftRect.isNull == false) { x = leftRect.maxX + 16 } let width : CGFloat = self.frame.width - 32 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX) if let unwrappedItems = items { for item in unwrappedItems { if let newItem = item as? IQTitleBarButtonItem { let titleRect = CGRect(x: x, y: 0, width: width, height: self.frame.size.height) newItem.customView?.frame = titleRect break } } } #endif } open var enableInputClicksWhenVisible: Bool { return true } } ================================================ FILE: Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift ================================================ // // IQUIView+IQKeyboardToolbar.swift // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-16 Iftekhar Qurashi. // // 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. import UIKit fileprivate func < (lhs: T?, rhs: T?) -> Bool { switch (lhs, rhs) { case let (l?, r?): return l < r case (nil, _?): return true default: return false } } fileprivate func > (lhs: T?, rhs: T?) -> Bool { switch (lhs, rhs) { case let (l?, r?): return l > r default: return rhs < lhs } } private var kIQShouldHideToolbarPlaceholder = "kIQShouldHideToolbarPlaceholder" private var kIQToolbarPlaceholder = "kIQToolbarPlaceholder" private var kIQKeyboardToolbar = "kIQKeyboardToolbar" /** UIView category methods to add IQToolbar on UIKeyboard. */ public extension UIView { /** IQToolbar references for better customization control. */ public var keyboardToolbar: IQToolbar { get { var toolbar = inputAccessoryView as? IQToolbar if (toolbar == nil) { toolbar = objc_getAssociatedObject(self, &kIQKeyboardToolbar) as? IQToolbar } if let unwrappedToolbar = toolbar { return unwrappedToolbar } else { let newToolbar = IQToolbar() objc_setAssociatedObject(self, &kIQKeyboardToolbar, newToolbar, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return newToolbar } } } ///------------------------- /// MARK: Title ///------------------------- /** If `shouldHideToolbarPlaceholder` is YES, then title will not be added to the toolbar. Default to NO. */ public var shouldHideToolbarPlaceholder: Bool { get { let aValue = objc_getAssociatedObject(self, &kIQShouldHideToolbarPlaceholder) as Any? if let unwrapedValue = aValue as? Bool { return unwrapedValue } else { return false } } set(newValue) { objc_setAssociatedObject(self, &kIQShouldHideToolbarPlaceholder, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder } } @available(*,deprecated, message: "This is renamed to `shouldHideToolbarPlaceholder` for more clear naming.") public var shouldHidePlaceholderText: Bool { get { return shouldHideToolbarPlaceholder } set(newValue) { shouldHideToolbarPlaceholder = newValue } } /** `toolbarPlaceholder` to override default `placeholder` text when drawing text on toolbar. */ public var toolbarPlaceholder: String? { get { let aValue = objc_getAssociatedObject(self, &kIQToolbarPlaceholder) as? String return aValue } set(newValue) { objc_setAssociatedObject(self, &kIQToolbarPlaceholder, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder } } @available(*,deprecated, message: "This is renamed to `toolbarPlaceholder` for more clear naming.") public var placeholderText: String? { get { return toolbarPlaceholder } set(newValue) { toolbarPlaceholder = newValue } } /** `drawingToolbarPlaceholder` will be actual text used to draw on toolbar. This would either `placeholder` or `toolbarPlaceholder`. */ public var drawingToolbarPlaceholder: String? { if (self.shouldHideToolbarPlaceholder) { return nil } else if (self.toolbarPlaceholder?.isEmpty == false) { return self.toolbarPlaceholder } else if self.responds(to: #selector(getter: UITextField.placeholder)) { if let textField = self as? UITextField { return textField.placeholder } else if let textView = self as? IQTextView { return textView.placeholder } else { return nil } } else { return nil } } @available(*,deprecated, message: "This is renamed to `drawingToolbarPlaceholder` for more clear naming.") public var drawingPlaceholderText: String? { return drawingToolbarPlaceholder } ///--------------------- /// MARK: Private helper ///--------------------- fileprivate static func flexibleBarButtonItem () -> IQBarButtonItem { struct Static { static let nilButton = IQBarButtonItem(barButtonSystemItem:UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil) } Static.nilButton.isSystemItem = true return Static.nilButton } ///------------ /// MARK: Done ///------------ /** Helper function to add Done button on keyboard. @param target Target object for selector. @param action Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. */ public func addDoneOnKeyboardWithTarget(_ target : AnyObject?, action : Selector) { addDoneOnKeyboardWithTarget(target, action: action, titleText: nil) } /** Helper function to add Done button on keyboard. @param target Target object for selector. @param action Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addDoneOnKeyboardWithTarget (_ target : AnyObject?, action : Selector, titleText: String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title button toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Done button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: target, action: action) doneButton.isSystemItem = true doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } /** Helper function to add Done button on keyboard. @param target Target object for selector. @param action Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. */ public func addDoneOnKeyboardWithTarget (_ target : AnyObject?, action : Selector, shouldShowPlaceholder: Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addDoneOnKeyboardWithTarget(target, action: action, titleText: title) } ///------------ /// MARK: Right ///------------ /** Helper function to add Right button on keyboard. @param image Image icon to use as right button. @param target Target object for selector. @param action Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addRightButtonOnKeyboardWithImage (_ image : UIImage, target : AnyObject?, action : Selector, titleText: String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title button toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Right button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton.title = nil doneButton.image = image doneButton.target = target doneButton.action = action } else { doneButton = IQBarButtonItem(image: image, style: UIBarButtonItemStyle.done, target: target, action: action) doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } /** Helper function to add Right button on keyboard. @param image Image icon to use as right button. @param target Target object for selector. @param action Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. */ public func addRightButtonOnKeyboardWithImage (_ image : UIImage, target : AnyObject?, action : Selector, shouldShowPlaceholder: Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addRightButtonOnKeyboardWithImage(image, target: target, action: action, titleText: title) } /** Helper function to add Right button on keyboard. @param text Title for rightBarButtonItem, usually 'Done'. @param target Target object for selector. @param action Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. */ public func addRightButtonOnKeyboardWithText (_ text : String, target : AnyObject?, action : Selector) { addRightButtonOnKeyboardWithText(text, target: target, action: action, titleText: nil) } /** Helper function to add Right button on keyboard. @param text Title for rightBarButtonItem, usually 'Done'. @param target Target object for selector. @param action Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addRightButtonOnKeyboardWithText (_ text : String, target : AnyObject?, action : Selector, titleText: String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title button toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Right button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton.title = text doneButton.image = nil doneButton.target = target doneButton.action = action } else { doneButton = IQBarButtonItem(title: text, style: UIBarButtonItemStyle.done, target: target, action: action) doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } /** Helper function to add Right button on keyboard. @param text Title for rightBarButtonItem, usually 'Done'. @param target Target object for selector. @param action Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. */ public func addRightButtonOnKeyboardWithText (_ text : String, target : AnyObject?, action : Selector, shouldShowPlaceholder: Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addRightButtonOnKeyboardWithText(text, target: target, action: action, titleText: title) } ///------------------ /// MARK: Cancel/Done ///------------------ /** Helper function to add Cancel and Done button on keyboard. @param target Target object for selector. @param cancelAction Cancel button action name. Usually 'cancelAction:(IQBarButtonItem*)item'. @param doneAction Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. */ public func addCancelDoneOnKeyboardWithTarget (_ target : AnyObject?, cancelAction : Selector, doneAction : Selector) { addCancelDoneOnKeyboardWithTarget(target, cancelAction: cancelAction, doneAction: doneAction, titleText: nil) } /** Helper function to add Cancel and Done button on keyboard. @param target Target object for selector. @param cancelAction Cancel button action name. Usually 'cancelAction:(IQBarButtonItem*)item'. @param doneAction Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addCancelDoneOnKeyboardWithTarget (_ target : AnyObject?, cancelAction : Selector, doneAction : Selector, titleText: String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] //Cancel button var cancelButton = toolbar.previousBarButton if cancelButton.isSystemItem == false { cancelButton = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: target, action: cancelAction) cancelButton.isSystemItem = true cancelButton.invocation = toolbar.previousBarButton.invocation cancelButton.accessibilityLabel = toolbar.previousBarButton.accessibilityLabel toolbar.previousBarButton = cancelButton } items.append(cancelButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Done button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: target, action: doneAction) doneButton.isSystemItem = true doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } /** Helper function to add Cancel and Done button on keyboard. @param target Target object for selector. @param cancelAction Cancel button action name. Usually 'cancelAction:(IQBarButtonItem*)item'. @param doneAction Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. */ public func addCancelDoneOnKeyboardWithTarget (_ target : AnyObject?, cancelAction : Selector, doneAction : Selector, shouldShowPlaceholder: Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addCancelDoneOnKeyboardWithTarget(target, cancelAction: cancelAction, doneAction: doneAction, titleText: title) } ///----------------- /// MARK: Right/Left ///----------------- /** Helper function to add Left and Right button on keyboard. @param target Target object for selector. @param leftButtonTitle Title for leftBarButtonItem, usually 'Cancel'. @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. @param leftButtonAction Left button action name. Usually 'cancelAction:(IQBarButtonItem*)item'. @param rightButtonAction Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. */ public func addRightLeftOnKeyboardWithTarget( _ target : AnyObject?, leftButtonTitle : String, rightButtonTitle : String, rightButtonAction : Selector, leftButtonAction : Selector) { addRightLeftOnKeyboardWithTarget(target, leftButtonTitle: leftButtonTitle, rightButtonTitle: rightButtonTitle, rightButtonAction: rightButtonAction, leftButtonAction: leftButtonAction, titleText: nil) } /** Helper function to add Left and Right button on keyboard. @param target Target object for selector. @param leftButtonTitle Title for leftBarButtonItem, usually 'Cancel'. @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. @param leftButtonAction Left button action name. Usually 'cancelAction:(IQBarButtonItem*)item'. @param rightButtonAction Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addRightLeftOnKeyboardWithTarget( _ target : AnyObject?, leftButtonTitle : String, rightButtonTitle : String, rightButtonAction : Selector, leftButtonAction : Selector, titleText: String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] //Left button var cancelButton = toolbar.previousBarButton if cancelButton.isSystemItem == false { cancelButton.title = rightButtonTitle cancelButton.image = nil cancelButton.target = target cancelButton.action = rightButtonAction } else { cancelButton = IQBarButtonItem(title: leftButtonTitle, style: UIBarButtonItemStyle.plain, target: target, action: leftButtonAction) cancelButton.invocation = toolbar.previousBarButton.invocation cancelButton.accessibilityLabel = toolbar.previousBarButton.accessibilityLabel toolbar.previousBarButton = cancelButton } items.append(cancelButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title button toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Right button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton.title = rightButtonTitle doneButton.image = nil doneButton.target = target doneButton.action = rightButtonAction } else { doneButton = IQBarButtonItem(title: rightButtonTitle, style: UIBarButtonItemStyle.done, target: target, action: rightButtonAction) doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } /** Helper function to add Left and Right button on keyboard. @param target Target object for selector. @param leftButtonTitle Title for leftBarButtonItem, usually 'Cancel'. @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. @param leftButtonAction Left button action name. Usually 'cancelAction:(IQBarButtonItem*)item'. @param rightButtonAction Right button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. */ public func addRightLeftOnKeyboardWithTarget( _ target : AnyObject?, leftButtonTitle : String, rightButtonTitle : String, rightButtonAction : Selector, leftButtonAction : Selector, shouldShowPlaceholder: Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addRightLeftOnKeyboardWithTarget(target, leftButtonTitle: leftButtonTitle, rightButtonTitle: rightButtonTitle, rightButtonAction: rightButtonAction, leftButtonAction: leftButtonAction, titleText: title) } ///------------------------- /// MARK: Previous/Next/Done ///------------------------- /** Helper function to add ArrowNextPrevious and Done button on keyboard. @param target Target object for selector. @param previousAction Previous button action name. Usually 'previousAction:(id)item'. @param nextAction Next button action name. Usually 'nextAction:(id)item'. @param doneAction Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. */ public func addPreviousNextDoneOnKeyboardWithTarget ( _ target : AnyObject?, previousAction : Selector, nextAction : Selector, doneAction : Selector) { addPreviousNextDoneOnKeyboardWithTarget(target, previousAction: previousAction, nextAction: nextAction, doneAction: doneAction, titleText: nil) } /** Helper function to add ArrowNextPrevious and Done button on keyboard. @param target Target object for selector. @param previousAction Previous button action name. Usually 'previousAction:(id)item'. @param nextAction Next button action name. Usually 'nextAction:(id)item'. @param doneAction Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addPreviousNextDoneOnKeyboardWithTarget ( _ target : AnyObject?, previousAction : Selector, nextAction : Selector, doneAction : Selector, titleText: String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] // Get the top level "bundle" which may actually be the framework var bundle = Bundle(for: IQKeyboardManager.self) if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") { if let resourcesBundle = Bundle(path: resourcePath) { bundle = resourcesBundle } } var imageLeftArrow : UIImage! var imageRightArrow : UIImage! if #available(iOS 10, *) { imageLeftArrow = UIImage(named: "IQButtonBarArrowUp", in: bundle, compatibleWith: nil) imageRightArrow = UIImage(named: "IQButtonBarArrowDown", in: bundle, compatibleWith: nil) } else { imageLeftArrow = UIImage(named: "IQButtonBarArrowLeft", in: bundle, compatibleWith: nil) imageRightArrow = UIImage(named: "IQButtonBarArrowRight", in: bundle, compatibleWith: nil) } //Support for RTL languages like Arabic, Persia etc... (Bug ID: #448) if #available(iOS 9, *) { imageLeftArrow = imageLeftArrow?.imageFlippedForRightToLeftLayoutDirection() imageRightArrow = imageRightArrow?.imageFlippedForRightToLeftLayoutDirection() } var prev = toolbar.previousBarButton if prev.isSystemItem == false { prev.title = nil prev.image = imageLeftArrow prev.target = target prev.action = previousAction } else { prev = IQBarButtonItem(image: imageLeftArrow, style: UIBarButtonItemStyle.plain, target: target, action: previousAction) prev.invocation = toolbar.previousBarButton.invocation prev.accessibilityLabel = toolbar.previousBarButton.accessibilityLabel toolbar.previousBarButton = prev } var next = toolbar.nextBarButton if next.isSystemItem == false { next.title = nil next.image = imageRightArrow next.target = target next.action = nextAction } else { next = IQBarButtonItem(image: imageRightArrow, style: UIBarButtonItemStyle.plain, target: target, action: nextAction) next.invocation = toolbar.nextBarButton.invocation next.accessibilityLabel = toolbar.nextBarButton.accessibilityLabel toolbar.nextBarButton = next } //Previous button items.append(prev) //Fixed space let fixed = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil) fixed.isSystemItem = true if #available(iOS 10, *) { fixed.width = 6 } else { fixed.width = 20 } items.append(fixed) //Next button items.append(next) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title button toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Done button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: target, action: doneAction) doneButton.isSystemItem = true doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } /** Helper function to add ArrowNextPrevious and Done button on keyboard. @param target Target object for selector. @param previousAction Previous button action name. Usually 'previousAction:(id)item'. @param nextAction Next button action name. Usually 'nextAction:(id)item'. @param doneAction Done button action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. */ public func addPreviousNextDoneOnKeyboardWithTarget ( _ target : AnyObject?, previousAction : Selector, nextAction : Selector, doneAction : Selector, shouldShowPlaceholder: Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addPreviousNextDoneOnKeyboardWithTarget(target, previousAction: previousAction, nextAction: nextAction, doneAction: doneAction, titleText: title) } ///-------------------------- /// MARK: Previous/Next/Right ///-------------------------- /** Helper function to add ArrowNextPrevious and Right button on keyboard. @param target Target object for selector. @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. @param previousAction Previous button action name. Usually 'previousAction:(id)item'. @param nextAction Next button action name. Usually 'nextAction:(id)item'. @param rightButtonAction RightBarButton action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addPreviousNextRightOnKeyboardWithTarget( _ target : AnyObject?, rightButtonImage : UIImage, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, titleText : String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] // Get the top level "bundle" which may actually be the framework var bundle = Bundle(for: IQKeyboardManager.self) if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") { if let resourcesBundle = Bundle(path: resourcePath) { bundle = resourcesBundle } } var imageLeftArrow : UIImage! var imageRightArrow : UIImage! if #available(iOS 10, *) { imageLeftArrow = UIImage(named: "IQButtonBarArrowUp", in: bundle, compatibleWith: nil) imageRightArrow = UIImage(named: "IQButtonBarArrowDown", in: bundle, compatibleWith: nil) } else { imageLeftArrow = UIImage(named: "IQButtonBarArrowLeft", in: bundle, compatibleWith: nil) imageRightArrow = UIImage(named: "IQButtonBarArrowRight", in: bundle, compatibleWith: nil) } //Support for RTL languages like Arabic, Persia etc... (Bug ID: #448) if #available(iOS 9, *) { imageLeftArrow = imageLeftArrow?.imageFlippedForRightToLeftLayoutDirection() imageRightArrow = imageRightArrow?.imageFlippedForRightToLeftLayoutDirection() } var prev = toolbar.previousBarButton if prev.isSystemItem == false { prev.title = nil prev.image = imageLeftArrow prev.target = target prev.action = previousAction } else { prev = IQBarButtonItem(image: imageLeftArrow, style: UIBarButtonItemStyle.plain, target: target, action: previousAction) prev.invocation = toolbar.previousBarButton.invocation prev.accessibilityLabel = toolbar.previousBarButton.accessibilityLabel toolbar.previousBarButton = prev } var next = toolbar.nextBarButton if next.isSystemItem == false { next.title = nil next.image = imageRightArrow next.target = target next.action = nextAction } else { next = IQBarButtonItem(image: imageRightArrow, style: UIBarButtonItemStyle.plain, target: target, action: nextAction) next.invocation = toolbar.nextBarButton.invocation next.accessibilityLabel = toolbar.nextBarButton.accessibilityLabel toolbar.nextBarButton = next } //Previous button items.append(prev) //Fixed space let fixed = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil) fixed.isSystemItem = true if #available(iOS 10, *) { fixed.width = 6 } else { fixed.width = 20 } items.append(fixed) //Next button items.append(next) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title button toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Right button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton.title = nil doneButton.image = rightButtonImage doneButton.target = target doneButton.action = rightButtonAction } else { doneButton = IQBarButtonItem(image: rightButtonImage, style: UIBarButtonItemStyle.done, target: target, action: rightButtonAction) doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } // /** // Helper function to add ArrowNextPrevious and Right button on keyboard. // // @param target Target object for selector. // @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. // @param previousAction Previous button action name. Usually 'previousAction:(id)item'. // @param nextAction Next button action name. Usually 'nextAction:(id)item'. // @param rightButtonAction RightBarButton action name. Usually 'doneAction:(IQBarButtonItem*)item'. // @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. // */ public func addPreviousNextRightOnKeyboardWithTarget( _ target : AnyObject?, rightButtonImage : UIImage, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, shouldShowPlaceholder : Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addPreviousNextRightOnKeyboardWithTarget(target, rightButtonImage: rightButtonImage, previousAction: previousAction, nextAction: nextAction, rightButtonAction: rightButtonAction, titleText: title) } /** Helper function to add ArrowNextPrevious and Right button on keyboard. @param target Target object for selector. @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. @param previousAction Previous button action name. Usually 'previousAction:(id)item'. @param nextAction Next button action name. Usually 'nextAction:(id)item'. @param rightButtonAction RightBarButton action name. Usually 'doneAction:(IQBarButtonItem*)item'. */ public func addPreviousNextRightOnKeyboardWithTarget( _ target : AnyObject?, rightButtonTitle : String, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector) { addPreviousNextRightOnKeyboardWithTarget(target, rightButtonTitle: rightButtonTitle, previousAction: previousAction, nextAction: nextAction, rightButtonAction: rightButtonAction, titleText: nil) } /** Helper function to add ArrowNextPrevious and Right button on keyboard. @param target Target object for selector. @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. @param previousAction Previous button action name. Usually 'previousAction:(id)item'. @param nextAction Next button action name. Usually 'nextAction:(id)item'. @param rightButtonAction RightBarButton action name. Usually 'doneAction:(IQBarButtonItem*)item'. @param titleText text to show as title in IQToolbar'. */ public func addPreviousNextRightOnKeyboardWithTarget( _ target : AnyObject?, rightButtonTitle : String, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, titleText : String?) { //If can't set InputAccessoryView. Then return if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) { // Creating a toolBar for phoneNumber keyboard let toolbar = self.keyboardToolbar var items : [IQBarButtonItem] = [] // Get the top level "bundle" which may actually be the framework var bundle = Bundle(for: IQKeyboardManager.self) if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") { if let resourcesBundle = Bundle(path: resourcePath) { bundle = resourcesBundle } } var imageLeftArrow : UIImage! var imageRightArrow : UIImage! if #available(iOS 10, *) { imageLeftArrow = UIImage(named: "IQButtonBarArrowUp", in: bundle, compatibleWith: nil) imageRightArrow = UIImage(named: "IQButtonBarArrowDown", in: bundle, compatibleWith: nil) } else { imageLeftArrow = UIImage(named: "IQButtonBarArrowLeft", in: bundle, compatibleWith: nil) imageRightArrow = UIImage(named: "IQButtonBarArrowRight", in: bundle, compatibleWith: nil) } //Support for RTL languages like Arabic, Persia etc... (Bug ID: #448) if #available(iOS 9, *) { imageLeftArrow = imageLeftArrow?.imageFlippedForRightToLeftLayoutDirection() imageRightArrow = imageRightArrow?.imageFlippedForRightToLeftLayoutDirection() } var prev = toolbar.previousBarButton if prev.isSystemItem == false { prev.title = nil prev.image = imageLeftArrow prev.target = target prev.action = previousAction } else { prev = IQBarButtonItem(image: imageLeftArrow, style: UIBarButtonItemStyle.plain, target: target, action: previousAction) prev.invocation = toolbar.previousBarButton.invocation prev.accessibilityLabel = toolbar.previousBarButton.accessibilityLabel toolbar.previousBarButton = prev } var next = toolbar.nextBarButton if next.isSystemItem == false { next.title = nil next.image = imageRightArrow next.target = target next.action = nextAction } else { next = IQBarButtonItem(image: imageRightArrow, style: UIBarButtonItemStyle.plain, target: target, action: nextAction) next.invocation = toolbar.nextBarButton.invocation next.accessibilityLabel = toolbar.nextBarButton.accessibilityLabel toolbar.nextBarButton = next } //Previous button items.append(prev) //Fixed space let fixed = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil) fixed.isSystemItem = true if #available(iOS 10, *) { fixed.width = 6 } else { fixed.width = 20 } items.append(fixed) //Next button items.append(next) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Title button toolbar.titleBarButton.title = shouldHideToolbarPlaceholder == true ? nil : titleText #if swift(>=3.2) if #available(iOS 11, *) {} else { toolbar.titleBarButton.customView?.frame = CGRect.zero } #else toolbar.titleBarButton.customView?.frame = CGRect.zero #endif items.append(toolbar.titleBarButton) //Flexible space items.append(UIView.flexibleBarButtonItem()) //Right button var doneButton = toolbar.doneBarButton if doneButton.isSystemItem == false { doneButton.title = rightButtonTitle doneButton.image = nil doneButton.target = target doneButton.action = rightButtonAction } else { doneButton = IQBarButtonItem(title: rightButtonTitle, style: UIBarButtonItemStyle.done, target: target, action: rightButtonAction) doneButton.invocation = toolbar.doneBarButton.invocation doneButton.accessibilityLabel = toolbar.doneBarButton.accessibilityLabel toolbar.doneBarButton = doneButton } items.append(doneButton) // Adding button to toolBar. toolbar.items = items // Setting toolbar to keyboard. if let textField = self as? UITextField { textField.inputAccessoryView = toolbar switch textField.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } else if let textView = self as? UITextView { textView.inputAccessoryView = toolbar switch textView.keyboardAppearance { case UIKeyboardAppearance.dark: toolbar.barStyle = UIBarStyle.black default: toolbar.barStyle = UIBarStyle.default } } } } // /** // Helper function to add ArrowNextPrevious and Right button on keyboard. // // @param target Target object for selector. // @param rightButtonTitle Title for rightBarButtonItem, usually 'Done'. // @param previousAction Previous button action name. Usually 'previousAction:(id)item'. // @param nextAction Next button action name. Usually 'nextAction:(id)item'. // @param rightButtonAction RightBarButton action name. Usually 'doneAction:(IQBarButtonItem*)item'. // @param shouldShowPlaceholder A boolean to indicate whether to show textField placeholder on IQToolbar'. // */ public func addPreviousNextRightOnKeyboardWithTarget( _ target : AnyObject?, rightButtonTitle : String, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, shouldShowPlaceholder : Bool) { var title : String? if shouldShowPlaceholder == true { title = self.drawingToolbarPlaceholder } addPreviousNextRightOnKeyboardWithTarget(target, rightButtonTitle: rightButtonTitle, previousAction: previousAction, nextAction: nextAction, rightButtonAction: rightButtonAction, titleText: title) } } ================================================ FILE: Pods/IQKeyboardManagerSwift/LICENSE.md ================================================ IQKeyboardManager license ========================= The MIT License (MIT) Copyright (c) 2013-16 Iftekhar Qurashi 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. ================================================ FILE: Pods/IQKeyboardManagerSwift/README.md ================================================

Icon

IQKeyboardManager

GitHub license [![Build Status](https://travis-ci.org/hackiftekhar/IQKeyboardManager.svg)](https://travis-ci.org/hackiftekhar/IQKeyboardManager) [![Coverage Status](http://img.shields.io/coveralls/hackiftekhar/IQKeyboardManager/master.svg)](https://coveralls.io/r/hackiftekhar/IQKeyboardManager?branch=master) [![Code Health](https://landscape.io/github/hackiftekhar/IQKeyboardManager/master/landscape.svg?style=flat)](https://landscape.io/github/hackiftekhar/IQKeyboardManager/master) Often while developing an app, We ran into an issues where the iPhone keyboard slide up and cover the `UITextField/UITextView`. `IQKeyboardManager` allows you to prevent issues of the keyboard sliding up and cover `UITextField/UITextView` without needing you to enter any code and no additional setup required. To use `IQKeyboardManager` you simply need to add source files to your project. #### Key Features [![Issue Stats](http://issuestats.com/github/hackiftekhar/iqkeyboardmanager/badge/pr?style=flat)](http://issuestats.com/github/hackiftekhar/iqkeyboardmanager) [![Issue Stats](http://issuestats.com/github/hackiftekhar/iqkeyboardmanager/badge/issue?style=flat)](http://issuestats.com/github/hackiftekhar/iqkeyboardmanager) 1) `**CODELESS**, Zero Lines Of Code` 2) `Works Automatically` 3) `No More UIScrollView` 4) `No More Subclasses` 5) `No More Manual Work` 6) `No More #imports` `IQKeyboardManager` works on all orientations, and with the toolbar. There are also nice optional features allowing you to customize the distance from the text field, add the next/previous done button as a keyboard UIToolbar, play sounds when the user navigations through the form and more. ## Screenshot [![IQKeyboardManager](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerScreenshot.png)](http://youtu.be/6nhLw6hju2A) [![Settings](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerSettings.png)](http://youtu.be/6nhLw6hju2A) ## GIF animation [![IQKeyboardManager](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManager.gif)](http://youtu.be/6nhLw6hju2A) ## Video IQKeyboardManager Demo Video ## Warning - **If you're planning to build SDK/library/framework and wants to handle UITextField/UITextView with IQKeyboardManager then you're totally going on wrong way.** I would never suggest to add IQKeyboardManager as dependency/adding/shipping with any third-party library, instead of adding IQKeyboardManager you should implement your custom solution to achieve same result. IQKeyboardManager is totally designed for projects to help developers for their convenience, it's not designed for adding/dependency/shipping with any third-party library, because **doing this could block adoption by other developers for their projects as well(who are not using IQKeyboardManager and implemented their custom solution to handle UITextField/UITextView thought the project).** - If IQKeybaordManager conflicts with other third-party library, then it's developer responsibility to enable/disable IQKeyboardManager when presenting/dismissing third-party library UI. Third-party libraries are not responsible to handle IQKeyboardManager. ## Requirements [![Platform iOS](https://img.shields.io/badge/Platform-iOS-blue.svg?style=fla)]() | | Language | Minimum iOS Target | Minimum Xcode Version | |------------------------|----------|--------------------|-----------------------| | IQKeyboardManager | Obj-C | iOS 8.0 | Xcode 8.2.1 | | IQKeyboardManagerSwift | Swift | iOS 8.0 | Xcode 8.2.1 | | Demo Project | | | Xcode 9.0 | **Note** - 3.3.7 is the last iOS 7 supported version. #### Swift versions support | Swift | Xcode | IQKeyboardManagerSwift | |-------------|-------|------------------------| | 4.X | 9.0 | >= 5.0.0 | | 4.0 | 9.0 | 5.0.0 | | 3.1 | 8.3 | 4.0.10 | | 3.0 (3.0.2) | 8.2 | 4.0.8 | | 2.2 or 2.3 | 7.3 | 4.0.5 | | 2.1.1 | 7.2 | 4.0.0 | | 2.0 | 7.0 | 3.3.3.1 | **Note** - `5.0.0` is backward compatible till Swift 3. Installation ========================== #### Installation with CocoaPods [![CocoaPods](https://img.shields.io/cocoapods/v/IQKeyboardManager.svg)](http://cocoadocs.org/docsets/IQKeyboardManager) ***IQKeyboardManager (Objective-C):*** IQKeyboardManager is available through [CocoaPods](http://cocoapods.org), to install it simply add the following line to your Podfile: ([#9](https://github.com/hackiftekhar/IQKeyboardManager/issues/9)) ```ruby `pod 'IQKeyboardManager'` #iOS8 and later `pod 'IQKeyboardManager', '3.3.7'` #iOS7 ``` ***IQKeyboardManager (Swift):*** IQKeyboardManagerSwift is available through [CocoaPods](http://cocoapods.org), to install it simply add the following line to your Podfile: ([#236](https://github.com/hackiftekhar/IQKeyboardManager/issues/236)) *Swift 4.0 (Xcode 9.0)* ```ruby pod 'IQKeyboardManagerSwift' ``` *Or you can choose version you need based on Swift support table from [Requirements](README.md#requirements)* ```ruby pod 'IQKeyboardManagerSwift', '5.0.0' ``` In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager. ```swift import IQKeyboardManagerSwift @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { IQKeyboardManager.sharedManager().enable = true return true } } ``` #### Installation with Carthage [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. You can install Carthage with [Homebrew](http://brew.sh/) using the following command: ```bash $ brew update $ brew install carthage ``` To integrate `IQKeyboardManger` or `IQKeyboardManagerSwift` into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl github "hackiftekhar/IQKeyboardManager" ``` Run `carthage` to build the frameworks and drag the appropriate framework (`IQKeyboardManager.framework` or `IQKeyboardManagerSwift.framework`) into your Xcode project according to your need. Make sure to add only one framework and not both. #### Installation with Source Code [![Github tag](https://img.shields.io/github/tag/hackiftekhar/iqkeyboardmanager.svg)]() ***IQKeyboardManager (Objective-C):*** Just ***drag and drop*** `IQKeyboardManager` directory from demo project to your project. That's it. ***IQKeyboardManager (Swift):*** ***Drag and drop*** `IQKeyboardManagerSwift` directory from demo project to your project In AppDelegate.swift, just enable IQKeyboardManager. ```swift @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { IQKeyboardManager.sharedManager().enable = true return true } } ``` ## Known Issues You can find known issues list [here](https://github.com/hackiftekhar/IQKeyboardManager/blob/master/KNOWN%20ISSUES.md). Manual Management: --- You can find some manual management tweaks & examples [here](https://github.com/hackiftekhar/IQKeyboardManager/blob/master/MANUAL%20MANAGEMENT.md). ## Flow Diagram [![IQKeyboardManager CFD](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/IQKeyboardManagerFlowDiagram.jpg)](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/IQKeyboardManagerFlowDiagram.jpg) If you would like to see detailed Flow diagram then see [here](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerCFD.jpg). ## Properties and functions usage You can find some documentation about properties, methods and their uses [here](https://github.com/hackiftekhar/IQKeyboardManager/blob/master/PROPERTIES%20%26%20FUNCTIONS.md). LICENSE --- Distributed under the MIT License. Contributions --- Any contribution is more than welcome! You can contribute through pull requests and issues on GitHub. Author --- If you wish to contact me, email at: hack.iftekhar@gmail.com ================================================ FILE: Pods/Kingfisher/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2017 Wei Wang 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. ================================================ FILE: Pods/Kingfisher/README.md ================================================

Kingfisher

codebeat badge

Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app. ## Features - [x] Asynchronous image downloading and caching. - [x] `URLSession`-based networking. Basic image processors and filters supplied. - [x] Multiple-layer cache for both memory and disk. - [x] Cancelable downloading and processing tasks to improve performance. - [x] Independent components. Use the downloader or caching system separately as you need. - [x] Prefetching images and showing them from cache later when necessary. - [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL. - [x] Built-in transition animation when setting images. - [x] Customizable placeholder while loading images. - [x] Extensible image processing and image format support. The simplest use-case is setting an image to an image view with the `UIImageView` extension: ```swift let url = URL(string: "url_of_your_image") imageView.kf.setImage(with: url) ``` Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately. ## Requirements - iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ - Swift 4 (Kingfisher 4.x), Swift 3 (Kingfisher 3.x) Main development of Kingfisher is based on Swift 4. Only critical bug fixes will be applied to Kingfisher 3.x. - Kingfisher 4.0 Migration - Kingfisher 3.x should be source compatible to Kingfisher 4. The reason for a major update is that we need to specify the Swift version explicitly for Xcode. All deprecated methods in Kingfisher 3 has been removed, so please ensure you have no warning left before you migrate from Kingfisher 3 to Kingfisher 4. If you have any trouble in migrating, please open an issue to discuss. - [Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information. ## Next Steps We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there. * [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project. * [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher! * [API Reference](http://onevcat.github.io/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference. ## Other ### Future of Kingfisher I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better. ### Developments and Tests Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. The test images are contained in another project to keep this project repo fast and slim. You could run `./setup.sh` in the root folder of Kingfisher to clone the test images when you need to run the tests target. It would be appreciated if your pull requests could build and with all tests green. :) ### About the logo The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions? ### Contact Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well. ## Contributors This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/onevcat/Kingfisher/blob/master/CONTRIBUTING.md). ## Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/Kingfisher#backer)] ## Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/Kingfisher#sponsor)] ### License Kingfisher is released under the MIT license. See LICENSE for details. ================================================ FILE: Pods/Kingfisher/Sources/AnimatedImageView.swift ================================================ // // AnimatableImageView.swift // Kingfisher // // Created by bl4ckra1sond3tre on 4/22/16. // // The AnimatableImageView, AnimatedFrame and Animator is a modified version of // some classes from kaishin's Gifu project (https://github.com/kaishin/Gifu) // // The MIT License (MIT) // // Copyright (c) 2017 Reda Lemeden. // // 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. // // The name and characters used in the demo of this software are property of their // respective owners. import UIKit import ImageIO /// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image. open class AnimatedImageView: UIImageView { /// Proxy object for prevending a reference cycle between the CADDisplayLink and AnimatedImageView. class TargetProxy { private weak var target: AnimatedImageView? init(target: AnimatedImageView) { self.target = target } @objc func onScreenUpdate() { target?.updateFrame() } } // MARK: - Public property /// Whether automatically play the animation when the view become visible. Default is true. public var autoPlayAnimatedImage = true /// The size of the frame cache. public var framePreloadCount = 10 /// Specifies whether the GIF frames should be pre-scaled to save memory. Default is true. public var needsPrescaling = true /// The animation timer's run loop mode. Default is `NSRunLoopCommonModes`. Set this property to `NSDefaultRunLoopMode` will make the animation pause during UIScrollView scrolling. public var runLoopMode = RunLoopMode.commonModes { willSet { if runLoopMode == newValue { return } else { stopAnimating() displayLink.remove(from: .main, forMode: runLoopMode) displayLink.add(to: .main, forMode: newValue) startAnimating() } } } // MARK: - Private property /// `Animator` instance that holds the frames of a specific image in memory. private var animator: Animator? /// A flag to avoid invalidating the displayLink on deinit if it was never created, because displayLink is so lazy. :D private var isDisplayLinkInitialized: Bool = false /// A display link that keeps calling the `updateFrame` method on every screen refresh. private lazy var displayLink: CADisplayLink = { self.isDisplayLinkInitialized = true let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate)) displayLink.add(to: .main, forMode: self.runLoopMode) displayLink.isPaused = true return displayLink }() // MARK: - Override override open var image: Image? { didSet { if image != oldValue { reset() } setNeedsDisplay() layer.setNeedsDisplay() } } deinit { if isDisplayLinkInitialized { displayLink.invalidate() } } override open var isAnimating: Bool { if isDisplayLinkInitialized { return !displayLink.isPaused } else { return super.isAnimating } } /// Starts the animation. override open func startAnimating() { if self.isAnimating { return } else { displayLink.isPaused = false } } /// Stops the animation. override open func stopAnimating() { super.stopAnimating() if isDisplayLinkInitialized { displayLink.isPaused = true } } override open func display(_ layer: CALayer) { if let currentFrame = animator?.currentFrame { layer.contents = currentFrame.cgImage } else { layer.contents = image?.cgImage } } override open func didMoveToWindow() { super.didMoveToWindow() didMove() } override open func didMoveToSuperview() { super.didMoveToSuperview() didMove() } // This is for back compatibility that using regular UIImageView to show animated image. override func shouldPreloadAllAnimation() -> Bool { return false } // MARK: - Private method /// Reset the animator. private func reset() { animator = nil if let imageSource = image?.kf.imageSource?.imageRef { animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount) animator?.needsPrescaling = needsPrescaling animator?.prepareFramesAsynchronously() } didMove() } private func didMove() { if autoPlayAnimatedImage && animator != nil { if let _ = superview, let _ = window { startAnimating() } else { stopAnimating() } } } /// Update the current frame with the displayLink duration. private func updateFrame() { let duration: CFTimeInterval // CA based display link is opt-out from ProMotion by default. // So the duration and its FPS might not match. // See [#718](https://github.com/onevcat/Kingfisher/issues/718) if #available(iOS 10.0, tvOS 10.0, *) { // By setting CADisableMinimumFrameDuration to YES in Info.plist may // cause the preferredFramesPerSecond being 0 if displayLink.preferredFramesPerSecond == 0 { duration = displayLink.duration } else { // Some devices (like iPad Pro 10.5) will have a different FPS. duration = 1.0 / Double(displayLink.preferredFramesPerSecond) } } else { duration = displayLink.duration } if animator?.updateCurrentFrame(duration: duration) ?? false { layer.setNeedsDisplay() } } } /// Keeps a reference to an `Image` instance and its duration as a GIF frame. struct AnimatedFrame { var image: Image? let duration: TimeInterval static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0) } // MARK: - Animator class Animator { // MARK: Private property fileprivate let size: CGSize fileprivate let maxFrameCount: Int fileprivate let imageSource: CGImageSource fileprivate var animatedFrames = [AnimatedFrame]() fileprivate let maxTimeStep: TimeInterval = 1.0 fileprivate var frameCount = 0 fileprivate var currentFrameIndex = 0 fileprivate var currentPreloadIndex = 0 fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0 fileprivate var needsPrescaling = true /// Loop count of animated image. private var loopCount = 0 var currentFrame: UIImage? { return frame(at: currentFrameIndex) } var contentMode = UIViewContentMode.scaleToFill private lazy var preloadQueue: DispatchQueue = { return DispatchQueue(label: "com.onevcat.Kingfisher.Animator.preloadQueue") }() /** Init an animator with image source reference. - parameter imageSource: The reference of animated image. - parameter contentMode: Content mode of AnimatedImageView. - parameter size: Size of AnimatedImageView. - parameter framePreloadCount: Frame cache size. - returns: The animator object. */ init(imageSource source: CGImageSource, contentMode mode: UIViewContentMode, size: CGSize, framePreloadCount count: Int) { self.imageSource = source self.contentMode = mode self.size = size self.maxFrameCount = count } func frame(at index: Int) -> Image? { return animatedFrames[safe: index]?.image } func prepareFramesAsynchronously() { preloadQueue.async { [weak self] in self?.prepareFrames() } } private func prepareFrames() { frameCount = CGImageSourceGetCount(imageSource) if let properties = CGImageSourceCopyProperties(imageSource, nil), let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary, let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int { self.loopCount = loopCount } let frameToProcess = min(frameCount, maxFrameCount) animatedFrames.reserveCapacity(frameToProcess) animatedFrames = (0.. AnimatedFrame { guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else { return AnimatedFrame.null } let defaultGIFFrameDuration = 0.100 let frameDuration = imageSource.kf.gifProperties(at: index).map { gifInfo -> Double in let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double? let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double? let duration = unclampedDelayTime ?? delayTime ?? 0.0 /** http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp Many annoying ads specify a 0 duration to make an image flash as quickly as possible. We follow Safari and Firefox's behavior and use a duration of 100 ms for any frames that specify a duration of <= 10 ms. See and for more information. See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser. */ return duration > 0.011 ? duration : defaultGIFFrameDuration } ?? defaultGIFFrameDuration let image = Image(cgImage: imageRef) let scaledImage: Image? if needsPrescaling { scaledImage = image.kf.resize(to: size, for: contentMode) } else { scaledImage = image } return AnimatedFrame(image: scaledImage, duration: frameDuration) } /** Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`. */ func updateCurrentFrame(duration: CFTimeInterval) -> Bool { timeSinceLastFrameChange += min(maxTimeStep, duration) guard let frameDuration = animatedFrames[safe: currentFrameIndex]?.duration, frameDuration <= timeSinceLastFrameChange else { return false } timeSinceLastFrameChange -= frameDuration let lastFrameIndex = currentFrameIndex currentFrameIndex += 1 currentFrameIndex = currentFrameIndex % animatedFrames.count if animatedFrames.count < frameCount { preloadFrameAsynchronously(at: lastFrameIndex) } return true } private func preloadFrameAsynchronously(at index: Int) { preloadQueue.async { [weak self] in self?.preloadFrame(at: index) } } private func preloadFrame(at index: Int) { animatedFrames[index] = prepareFrame(at: currentPreloadIndex) currentPreloadIndex += 1 currentPreloadIndex = currentPreloadIndex % frameCount } } extension CGImageSource: KingfisherCompatible { } extension Kingfisher where Base: CGImageSource { func gifProperties(at index: Int) -> [String: Double]? { let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary? return properties?[kCGImagePropertyGIFDictionary] as? [String: Double] } } extension Array { subscript(safe index: Int) -> Element? { return indices ~= index ? self[index] : nil } } private func pure(_ value: T) -> [T] { return [value] } ================================================ FILE: Pods/Kingfisher/Sources/Box.swift ================================================ // // Box.swift // Kingfisher // // Created by WANG WEI on 2016/09/12. // Copyright © 2016年 Wei Wang. All rights reserved. // import Foundation class Box { let value: T init(value: T) { self.value = value } } ================================================ FILE: Pods/Kingfisher/Sources/CacheSerializer.swift ================================================ // // CacheSerializer.swift // Kingfisher // // Created by Wei Wang on 2016/09/02. // // Copyright (c) 2017 Wei Wang // // 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. import Foundation /// An `CacheSerializer` would be used to convert some data to an image object for /// retrieving from disk cache and vice versa for storing to disk cache. public protocol CacheSerializer { /// Get the serialized data from a provided image /// and optional original data for caching to disk. /// /// /// - parameter image: The image needed to be serialized. /// - parameter original: The original data which is just downloaded. /// If the image is retrieved from cache instead of /// downloaded, it will be `nil`. /// /// - returns: A data which will be stored to cache, or `nil` when no valid /// data could be serialized. func data(with image: Image, original: Data?) -> Data? /// Get an image deserialized from provided data. /// /// - parameter data: The data from which an image should be deserialized. /// - parameter options: Options for deserialization. /// /// - returns: An image deserialized or `nil` when no valid image /// could be deserialized. func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? } /// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of /// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For /// image other than these formats, a normalized `pngRepresentation` will be used. public struct DefaultCacheSerializer: CacheSerializer { public static let `default` = DefaultCacheSerializer() private init() {} public func data(with image: Image, original: Data?) -> Data? { let imageFormat = original?.kf.imageFormat ?? .unknown let data: Data? switch imageFormat { case .PNG: data = image.kf.pngRepresentation() case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) case .GIF: data = image.kf.gifRepresentation() case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() } return data } public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { let options = options ?? KingfisherEmptyOptionsInfo return Kingfisher.image( data: data, scale: options.scaleFactor, preloadAllAnimationData: options.preloadAllAnimationData, onlyFirstFrame: options.onlyLoadFirstFrame) } } ================================================ FILE: Pods/Kingfisher/Sources/Filter.swift ================================================ // // Filter.swift // Kingfisher // // Created by Wei Wang on 2016/08/31. // // Copyright (c) 2017 Wei Wang // // 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. import CoreImage import Accelerate // Reuse the same CI Context for all CI drawing. private let ciContext = CIContext(options: nil) /// Transformer method which will be used in to provide a `Filter`. public typealias Transformer = (CIImage) -> CIImage? /// Supply a filter to create an `ImageProcessor`. public protocol CIImageProcessor: ImageProcessor { var filter: Filter { get } } extension CIImageProcessor { public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): return image.kf.apply(filter) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Wrapper for a `Transformer` of CIImage filters. public struct Filter { let transform: Transformer public init(tranform: @escaping Transformer) { self.transform = tranform } /// Tint filter which will apply a tint color to images. public static var tint: (Color) -> Filter = { color in Filter { input in let colorFilter = CIFilter(name: "CIConstantColorGenerator")! colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) let colorImage = colorFilter.outputImage let filter = CIFilter(name: "CISourceOverCompositing")! filter.setValue(colorImage, forKey: kCIInputImageKey) filter.setValue(input, forKey: kCIInputBackgroundImageKey) #if swift(>=4.0) return filter.outputImage?.cropped(to: input.extent) #else return filter.outputImage?.cropping(to: input.extent) #endif } } public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) /// Color control filter which will apply color control change to images. public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in let (brightness, contrast, saturation, inputEV) = arg return Filter { input in let paramsColor = [kCIInputBrightnessKey: brightness, kCIInputContrastKey: contrast, kCIInputSaturationKey: saturation] let paramsExposure = [kCIInputEVKey: inputEV] #if swift(>=4.0) let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor) return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure) #else let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor) return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure) #endif } } } extension Kingfisher where Base: Image { /// Apply a `Filter` containing `CIImage` transformer to `self`. /// /// - parameter filter: The filter used to transform `self`. /// /// - returns: A transformed image by input `Filter`. /// /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. public func apply(_ filter: Filter) -> Image { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Tint image only works for CG-based image.") return base } let inputImage = CIImage(cgImage: cgImage) guard let outputImage = filter.transform(inputImage) else { return base } guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { assertionFailure("[Kingfisher] Can not make an tint image within context.") return base } #if os(macOS) return fixedForRetinaPixel(cgImage: result, to: size) #else return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) #endif } } ================================================ FILE: Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift ================================================ // // RequestModifier.swift // Kingfisher // // Created by Junyu Kuang on 5/28/17. // // Copyright (c) 2017 Wei Wang // // 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. import Foundation /// `FormatIndicatedCacheSerializer` let you indicate an image format for serialized caches. /// /// It could serialize and deserialize PNG, JEPG and GIF images. For /// image other than these formats, a normalized `pngRepresentation` will be used. /// /// Example: /// ```` /// private let profileImageSize = CGSize(width: 44, height: 44) /// /// private let imageProcessor = RoundCornerImageProcessor( /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize) /// /// private let optionsInfo: KingfisherOptionsInfo = [ /// .cacheSerializer(FormatIndicatedCacheSerializer.png), /// .backgroundDecode, .processor(imageProcessor), .scaleFactor(UIScreen.main.scale)] /// /// extension UIImageView { /// func setProfileImage(with url: URL) { /// // Image will always cached as PNG format to preserve alpha channel for round rect. /// _ = kf.setImage(with: url, options: optionsInfo) /// } ///} /// ```` public struct FormatIndicatedCacheSerializer: CacheSerializer { public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG) public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG) public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF) /// The indicated image format. private let imageFormat: ImageFormat public func data(with image: Image, original: Data?) -> Data? { func imageData(withFormat imageFormat: ImageFormat) -> Data? { switch imageFormat { case .PNG: return image.kf.pngRepresentation() case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0) case .GIF: return image.kf.gifRepresentation() case .unknown: return nil } } // generate data with indicated image format if let data = imageData(withFormat: imageFormat) { return data } let originalFormat = original?.kf.imageFormat ?? .unknown // generate data with original image's format if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) { return data } return original ?? image.kf.normalized.kf.pngRepresentation() } /// Same implementation as `DefaultCacheSerializer`. public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { let options = options ?? KingfisherEmptyOptionsInfo return Kingfisher.image( data: data, scale: options.scaleFactor, preloadAllAnimationData: options.preloadAllAnimationData, onlyFirstFrame: options.onlyLoadFirstFrame) } } ================================================ FILE: Pods/Kingfisher/Sources/Image.swift ================================================ // // Image.swift // Kingfisher // // Created by Wei Wang on 16/1/6. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit private var imagesKey: Void? private var durationKey: Void? #else import UIKit import MobileCoreServices private var imageSourceKey: Void? #endif private var animatedImageDataKey: Void? import ImageIO import CoreGraphics #if !os(watchOS) import Accelerate import CoreImage #endif // MARK: - Image Properties extension Kingfisher where Base: Image { fileprivate(set) var animatedImageData: Data? { get { return objc_getAssociatedObject(base, &animatedImageDataKey) as? Data } set { objc_setAssociatedObject(base, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } #if os(macOS) var cgImage: CGImage? { return base.cgImage(forProposedRect: nil, context: nil, hints: nil) } var scale: CGFloat { return 1.0 } fileprivate(set) var images: [Image]? { get { return objc_getAssociatedObject(base, &imagesKey) as? [Image] } set { objc_setAssociatedObject(base, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } fileprivate(set) var duration: TimeInterval { get { return objc_getAssociatedObject(base, &durationKey) as? TimeInterval ?? 0.0 } set { objc_setAssociatedObject(base, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } var size: CGSize { return base.representations.reduce(CGSize.zero, { size, rep in return CGSize(width: max(size.width, CGFloat(rep.pixelsWide)), height: max(size.height, CGFloat(rep.pixelsHigh))) }) } #else var cgImage: CGImage? { return base.cgImage } var scale: CGFloat { return base.scale } var images: [Image]? { return base.images } var duration: TimeInterval { return base.duration } fileprivate(set) var imageSource: ImageSource? { get { return objc_getAssociatedObject(base, &imageSourceKey) as? ImageSource } set { objc_setAssociatedObject(base, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } var size: CGSize { return base.size } #endif } // MARK: - Image Conversion extension Kingfisher where Base: Image { #if os(macOS) static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { return Image(cgImage: cgImage, size: CGSize.zero) } /** Normalize the image. This method does nothing in OS X. - returns: The image itself. */ public var normalized: Image { return base } static func animated(with images: [Image], forDuration forDurationduration: TimeInterval) -> Image? { return nil } #else static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { if let refImage = refImage { return Image(cgImage: cgImage, scale: scale, orientation: refImage.imageOrientation) } else { return Image(cgImage: cgImage, scale: scale, orientation: .up) } } /** Normalize the image. This method will try to redraw an image with orientation and scale considered. - returns: The normalized image with orientation set to up and correct scale. */ public var normalized: Image { // prevent animated image (GIF) lose it's images guard images == nil else { return base } // No need to do anything if already up guard base.imageOrientation != .up else { return base } return draw(cgImage: nil, to: size) { base.draw(in: CGRect(origin: CGPoint.zero, size: size)) } } static func animated(with images: [Image], forDuration duration: TimeInterval) -> Image? { return .animatedImage(with: images, duration: duration) } #endif } // MARK: - Image Representation extension Kingfisher where Base: Image { // MARK: - PNG public func pngRepresentation() -> Data? { #if os(macOS) guard let cgimage = cgImage else { return nil } let rep = NSBitmapImageRep(cgImage: cgimage) return rep.representation(using: .png, properties: [:]) #else return UIImagePNGRepresentation(base) #endif } // MARK: - JPEG public func jpegRepresentation(compressionQuality: CGFloat) -> Data? { #if os(macOS) guard let cgImage = cgImage else { return nil } let rep = NSBitmapImageRep(cgImage: cgImage) return rep.representation(using:.jpeg, properties: [.compressionFactor: compressionQuality]) #else return UIImageJPEGRepresentation(base, compressionQuality) #endif } // MARK: - GIF public func gifRepresentation() -> Data? { return animatedImageData } } // MARK: - Create images from data extension Kingfisher where Base: Image { static func animated(with data: Data, scale: CGFloat = 1.0, duration: TimeInterval = 0.0, preloadAll: Bool, onlyFirstFrame: Bool = false) -> Image? { func decode(from imageSource: CGImageSource, for options: NSDictionary) -> ([Image], TimeInterval)? { //Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary func frameDuration(from gifInfo: NSDictionary?) -> Double { let gifDefaultFrameDuration = 0.100 guard let gifInfo = gifInfo else { return gifDefaultFrameDuration } let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber let duration = unclampedDelayTime ?? delayTime guard let frameDuration = duration else { return gifDefaultFrameDuration } return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : gifDefaultFrameDuration } let frameCount = CGImageSourceGetCount(imageSource) var images = [Image]() var gifDuration = 0.0 for i in 0 ..< frameCount { guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else { return nil } if frameCount == 1 { // Single frame gifDuration = Double.infinity } else { // Animated GIF guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) else { return nil } let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary gifDuration += frameDuration(from: gifInfo) } images.append(Kingfisher.image(cgImage: imageRef, scale: scale, refImage: nil)) if onlyFirstFrame { break } } return (images, gifDuration) } // Start of kf.animatedImageWithGIFData let options: NSDictionary = [kCGImageSourceShouldCache as String: true, kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF] guard let imageSource = CGImageSourceCreateWithData(data as CFData, options) else { return nil } #if os(macOS) guard let (images, gifDuration) = decode(from: imageSource, for: options) else { return nil } let image: Image? if onlyFirstFrame { image = images.first } else { image = Image(data: data) image?.kf.images = images image?.kf.duration = gifDuration } image?.kf.animatedImageData = data return image #else let image: Image? if preloadAll || onlyFirstFrame { guard let (images, gifDuration) = decode(from: imageSource, for: options) else { return nil } image = onlyFirstFrame ? images.first : Kingfisher.animated(with: images, forDuration: duration <= 0.0 ? gifDuration : duration) } else { image = Image(data: data) image?.kf.imageSource = ImageSource(ref: imageSource) } image?.kf.animatedImageData = data return image #endif } static func image(data: Data, scale: CGFloat, preloadAllAnimationData: Bool, onlyFirstFrame: Bool) -> Image? { var image: Image? #if os(macOS) switch data.kf.imageFormat { case .JPEG: image = Image(data: data) case .PNG: image = Image(data: data) case .GIF: image = Kingfisher.animated( with: data, scale: scale, duration: 0.0, preloadAll: preloadAllAnimationData, onlyFirstFrame: onlyFirstFrame) case .unknown: image = Image(data: data) } #else switch data.kf.imageFormat { case .JPEG: image = Image(data: data, scale: scale) case .PNG: image = Image(data: data, scale: scale) case .GIF: image = Kingfisher.animated( with: data, scale: scale, duration: 0.0, preloadAll: preloadAllAnimationData, onlyFirstFrame: onlyFirstFrame) case .unknown: image = Image(data: data, scale: scale) } #endif return image } } // MARK: - Image Transforming extension Kingfisher where Base: Image { // MARK: - Round Corner /// Create a round corner image based on `self`. /// /// - parameter radius: The round corner radius of creating image. /// - parameter size: The target size of creating image. /// - parameter corners: The target corners which will be applied rounding. /// - parameter backgroundColor: The background color for the output image /// /// - returns: An image with round corner of `self`. /// /// - Note: This method only works for CG-based image. public func image(withRoundRadius radius: CGFloat, fit size: CGSize, roundingCorners corners: RectCorner = .all, backgroundColor: Color? = nil) -> Image { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Round corner image only works for CG-based image.") return base } let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) return draw(cgImage: cgImage, to: size) { #if os(macOS) if let backgroundColor = backgroundColor { let rectPath = NSBezierPath(rect: rect) backgroundColor.setFill() rectPath.fill() } let path = NSBezierPath(roundedRect: rect, byRoundingCorners: corners, radius: radius) path.windingRule = .evenOddWindingRule path.addClip() base.draw(in: rect) #else guard let context = UIGraphicsGetCurrentContext() else { assertionFailure("[Kingfisher] Failed to create CG context for image.") return } if let backgroundColor = backgroundColor { let rectPath = UIBezierPath(rect: rect) backgroundColor.setFill() rectPath.fill() } let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners.uiRectCorner, cornerRadii: CGSize(width: radius, height: radius)).cgPath context.addPath(path) context.clip() base.draw(in: rect) #endif } } #if os(iOS) || os(tvOS) func resize(to size: CGSize, for contentMode: UIViewContentMode) -> Image { switch contentMode { case .scaleAspectFit: return resize(to: size, for: .aspectFit) case .scaleAspectFill: return resize(to: size, for: .aspectFill) default: return resize(to: size) } } #endif // MARK: - Resize /// Resize `self` to an image of new size. /// /// - parameter size: The target size. /// /// - returns: An image with new size. /// /// - Note: This method only works for CG-based image. public func resize(to size: CGSize) -> Image { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Resize only works for CG-based image.") return base } let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) return draw(cgImage: cgImage, to: size) { #if os(macOS) base.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) #else base.draw(in: rect) #endif } } /// Resize `self` to an image of new size, respecting the content mode. /// /// - Parameters: /// - size: The target size. /// - contentMode: Content mode of output image should be. /// - Returns: An image with new size. public func resize(to size: CGSize, for contentMode: ContentMode) -> Image { switch contentMode { case .aspectFit: let newSize = self.size.kf.constrained(size) return resize(to: newSize) case .aspectFill: let newSize = self.size.kf.filling(size) return resize(to: newSize) default: return resize(to: size) } } public func crop(to size: CGSize, anchorOn anchor: CGPoint) -> Image { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Crop only works for CG-based image.") return base } let rect = self.size.kf.constrainedRect(for: size, anchor: anchor) guard let image = cgImage.cropping(to: rect.scaled(scale)) else { assertionFailure("[Kingfisher] Cropping image failed.") return base } return Kingfisher.image(cgImage: image, scale: scale, refImage: base) } // MARK: - Blur /// Create an image with blur effect based on `self`. /// /// - parameter radius: The blur radius should be used when creating blur effect. /// /// - returns: An image with blur effect applied. /// /// - Note: This method only works for CG-based image. public func blurred(withRadius radius: CGFloat) -> Image { #if os(watchOS) return base #else guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Blur only works for CG-based image.") return base } // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) // if d is odd, use three box-blurs of size 'd', centered on the output pixel. let s = Float(max(radius, 2.0)) // We will do blur on a resized image (*0.5), so the blur radius could be half as well. // Fix the slow compiling time for Swift 3. // See https://github.com/onevcat/Kingfisher/issues/611 let pi2 = 2 * Float.pi let sqrtPi2 = sqrt(pi2) var targetRadius = floor(s * 3.0 * sqrtPi2 / 4.0 + 0.5) if targetRadius.isEven { targetRadius += 1 } let iterations: Int if radius < 0.5 { iterations = 1 } else if radius < 1.5 { iterations = 2 } else { iterations = 3 } let w = Int(size.width) let h = Int(size.height) let rowBytes = Int(CGFloat(cgImage.bytesPerRow)) func createEffectBuffer(_ context: CGContext) -> vImage_Buffer { let data = context.data let width = vImagePixelCount(context.width) let height = vImagePixelCount(context.height) let rowBytes = context.bytesPerRow return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes) } guard let context = beginContext(size: size, scale: scale) else { assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") return base } defer { endContext() } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h)) var inBuffer = createEffectBuffer(context) guard let outContext = beginContext(size: size, scale: scale) else { assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") return base } defer { endContext() } var outBuffer = createEffectBuffer(outContext) for _ in 0 ..< iterations { vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, UInt32(targetRadius), UInt32(targetRadius), nil, vImage_Flags(kvImageEdgeExtend)) (inBuffer, outBuffer) = (outBuffer, inBuffer) } #if os(macOS) let result = outContext.makeImage().flatMap { fixedForRetinaPixel(cgImage: $0, to: size) } #else let result = outContext.makeImage().flatMap { Image(cgImage: $0, scale: base.scale, orientation: base.imageOrientation) } #endif guard let blurredImage = result else { assertionFailure("[Kingfisher] Can not make an blurred image within this context.") return base } return blurredImage #endif } // MARK: - Overlay /// Create an image from `self` with a color overlay layer. /// /// - parameter color: The color should be use to overlay. /// - parameter fraction: Fraction of input color. From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay. /// /// - returns: An image with a color overlay applied. /// /// - Note: This method only works for CG-based image. public func overlaying(with color: Color, fraction: CGFloat) -> Image { guard let cgImage = cgImage else { assertionFailure("[Kingfisher] Overlaying only works for CG-based image.") return base } let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) return draw(cgImage: cgImage, to: rect.size) { #if os(macOS) base.draw(in: rect) if fraction > 0 { color.withAlphaComponent(1 - fraction).set() rect.fill(using: .sourceAtop) } #else color.set() UIRectFill(rect) base.draw(in: rect, blendMode: .destinationIn, alpha: 1.0) if fraction > 0 { base.draw(in: rect, blendMode: .sourceAtop, alpha: fraction) } #endif } } // MARK: - Tint /// Create an image from `self` with a color tint. /// /// - parameter color: The color should be used to tint `self` /// /// - returns: An image with a color tint applied. public func tinted(with color: Color) -> Image { #if os(watchOS) return base #else return apply(.tint(color)) #endif } // MARK: - Color Control /// Create an image from `self` with color control. /// /// - parameter brightness: Brightness changing to image. /// - parameter contrast: Contrast changing to image. /// - parameter saturation: Saturation changing to image. /// - parameter inputEV: InputEV changing to image. /// /// - returns: An image with color control applied. public func adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { #if os(watchOS) return base #else return apply(.colorControl((brightness, contrast, saturation, inputEV))) #endif } } // MARK: - Decode extension Kingfisher where Base: Image { var decoded: Image { return decoded(scale: scale) } func decoded(scale: CGFloat) -> Image { // prevent animated image (GIF) lose it's images #if os(iOS) if imageSource != nil { return base } #else if images != nil { return base } #endif guard let imageRef = self.cgImage else { assertionFailure("[Kingfisher] Decoding only works for CG-based image.") return base } // Draw CGImage in a plain context with scale of 1.0. guard let context = beginContext(size: CGSize(width: imageRef.width, height: imageRef.height), scale: 1.0) else { assertionFailure("[Kingfisher] Decoding fails to create a valid context.") return base } defer { endContext() } let rect = CGRect(x: 0, y: 0, width: CGFloat(imageRef.width), height: CGFloat(imageRef.height)) context.draw(imageRef, in: rect) let decompressedImageRef = context.makeImage() return Kingfisher.image(cgImage: decompressedImageRef!, scale: scale, refImage: base) } } /// Reference the source image reference class ImageSource { var imageRef: CGImageSource? init(ref: CGImageSource) { self.imageRef = ref } } // MARK: - Image format private struct ImageHeaderData { static var PNG: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] static var JPEG_SOI: [UInt8] = [0xFF, 0xD8] static var JPEG_IF: [UInt8] = [0xFF] static var GIF: [UInt8] = [0x47, 0x49, 0x46] } enum ImageFormat { case unknown, PNG, JPEG, GIF } // MARK: - Misc Helpers public struct DataProxy { fileprivate let base: Data init(proxy: Data) { base = proxy } } extension Data: KingfisherCompatible { public typealias CompatibleType = DataProxy public var kf: DataProxy { return DataProxy(proxy: self) } } extension DataProxy { var imageFormat: ImageFormat { var buffer = [UInt8](repeating: 0, count: 8) (base as NSData).getBytes(&buffer, length: 8) if buffer == ImageHeaderData.PNG { return .PNG } else if buffer[0] == ImageHeaderData.JPEG_SOI[0] && buffer[1] == ImageHeaderData.JPEG_SOI[1] && buffer[2] == ImageHeaderData.JPEG_IF[0] { return .JPEG } else if buffer[0] == ImageHeaderData.GIF[0] && buffer[1] == ImageHeaderData.GIF[1] && buffer[2] == ImageHeaderData.GIF[2] { return .GIF } return .unknown } } public struct CGSizeProxy { fileprivate let base: CGSize init(proxy: CGSize) { base = proxy } } extension CGSize: KingfisherCompatible { public typealias CompatibleType = CGSizeProxy public var kf: CGSizeProxy { return CGSizeProxy(proxy: self) } } extension CGSizeProxy { func constrained(_ size: CGSize) -> CGSize { let aspectWidth = round(aspectRatio * size.height) let aspectHeight = round(size.width / aspectRatio) return aspectWidth > size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) } func filling(_ size: CGSize) -> CGSize { let aspectWidth = round(aspectRatio * size.height) let aspectHeight = round(size.width / aspectRatio) return aspectWidth < size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) } private var aspectRatio: CGFloat { return base.height == 0.0 ? 1.0 : base.width / base.height } func constrainedRect(for size: CGSize, anchor: CGPoint) -> CGRect { let unifiedAnchor = CGPoint(x: anchor.x.clamped(to: 0.0...1.0), y: anchor.y.clamped(to: 0.0...1.0)) let x = unifiedAnchor.x * base.width - unifiedAnchor.x * size.width let y = unifiedAnchor.y * base.height - unifiedAnchor.y * size.height let r = CGRect(x: x, y: y, width: size.width, height: size.height) let ori = CGRect(origin: CGPoint.zero, size: base) return ori.intersection(r) } } extension CGRect { func scaled(_ scale: CGFloat) -> CGRect { return CGRect(x: origin.x * scale, y: origin.y * scale, width: size.width * scale, height: size.height * scale) } } extension Comparable { func clamped(to limits: ClosedRange) -> Self { return min(max(self, limits.lowerBound), limits.upperBound) } } extension Kingfisher where Base: Image { func beginContext(size: CGSize, scale: CGFloat) -> CGContext? { #if os(macOS) guard let rep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: cgImage?.bitsPerComponent ?? 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0) else { assertionFailure("[Kingfisher] Image representation cannot be created.") return nil } rep.size = size NSGraphicsContext.saveGraphicsState() guard let context = NSGraphicsContext(bitmapImageRep: rep) else { assertionFailure("[Kingfisher] Image contenxt cannot be created.") return nil } NSGraphicsContext.current = context return context.cgContext #else UIGraphicsBeginImageContextWithOptions(size, false, scale) let context = UIGraphicsGetCurrentContext() context?.scaleBy(x: 1.0, y: -1.0) context?.translateBy(x: 0, y: -size.height) return context #endif } func endContext() { #if os(macOS) NSGraphicsContext.restoreGraphicsState() #else UIGraphicsEndImageContext() #endif } func draw(cgImage: CGImage?, to size: CGSize, draw: ()->()) -> Image { #if os(macOS) guard let rep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: cgImage?.bitsPerComponent ?? 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0) else { assertionFailure("[Kingfisher] Image representation cannot be created.") return base } rep.size = size NSGraphicsContext.saveGraphicsState() let context = NSGraphicsContext(bitmapImageRep: rep) NSGraphicsContext.current = context draw() NSGraphicsContext.restoreGraphicsState() let outputImage = Image(size: size) outputImage.addRepresentation(rep) return outputImage #else UIGraphicsBeginImageContextWithOptions(size, false, scale) defer { UIGraphicsEndImageContext() } draw() return UIGraphicsGetImageFromCurrentImageContext() ?? base #endif } #if os(macOS) func fixedForRetinaPixel(cgImage: CGImage, to size: CGSize) -> Image { let image = Image(cgImage: cgImage, size: base.size) let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) return draw(cgImage: cgImage, to: self.size) { image.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) } } #endif } extension Float { var isEven: Bool { return truncatingRemainder(dividingBy: 2.0) == 0 } } #if os(macOS) extension NSBezierPath { convenience init(roundedRect rect: NSRect, topLeftRadius: CGFloat, topRightRadius: CGFloat, bottomLeftRadius: CGFloat, bottomRightRadius: CGFloat) { self.init() let maxCorner = min(rect.width, rect.height) / 2 let radiusTopLeft = min(maxCorner, max(0, topLeftRadius)) let radiustopRight = min(maxCorner, max(0, topRightRadius)) let radiusbottomLeft = min(maxCorner, max(0, bottomLeftRadius)) let radiusbottomRight = min(maxCorner, max(0, bottomRightRadius)) guard !NSIsEmptyRect(rect) else { return } let topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect)); let topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect)); let bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect)); move(to: NSMakePoint(NSMidX(rect), NSMaxY(rect))) appendArc(from: topLeft, to: rect.origin, radius: radiusTopLeft) appendArc(from: rect.origin, to: bottomRight, radius: radiusbottomLeft) appendArc(from: bottomRight, to: topRight, radius: radiusbottomRight) appendArc(from: topRight, to: topLeft, radius: radiustopRight) close() } convenience init(roundedRect rect: NSRect, byRoundingCorners corners: RectCorner, radius: CGFloat) { let radiusTopLeft = corners.contains(.topLeft) ? radius : 0 let radiusTopRight = corners.contains(.topRight) ? radius : 0 let radiusBottomLeft = corners.contains(.bottomLeft) ? radius : 0 let radiusBottomRight = corners.contains(.bottomRight) ? radius : 0 self.init(roundedRect: rect, topLeftRadius: radiusTopLeft, topRightRadius: radiusTopRight, bottomLeftRadius: radiusBottomLeft, bottomRightRadius: radiusBottomRight) } } #else extension RectCorner { var uiRectCorner: UIRectCorner { var result: UIRectCorner = [] if self.contains(.topLeft) { result.insert(.topLeft) } if self.contains(.topRight) { result.insert(.topRight) } if self.contains(.bottomLeft) { result.insert(.bottomLeft) } if self.contains(.bottomRight) { result.insert(.bottomRight) } return result } } #endif ================================================ FILE: Pods/Kingfisher/Sources/ImageCache.swift ================================================ // // ImageCache.swift // Kingfisher // // Created by Wei Wang on 15/4/6. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif public extension Notification.Name { /** This notification will be sent when the disk cache got cleaned either there are cached files expired or the total size exceeding the max allowed size. The manually invoking of `clearDiskCache` method will not trigger this notification. The `object` of this notification is the `ImageCache` object which sends the notification. A list of removed hashes (files) could be retrieved by accessing the array under `KingfisherDiskCacheCleanedHashKey` key in `userInfo` of the notification object you received. By checking the array, you could know the hash codes of files are removed. The main purpose of this notification is supplying a chance to maintain some necessary information on the cached files. See [this wiki](https://github.com/onevcat/Kingfisher/wiki/How-to-implement-ETag-based-304-(Not-Modified)-handling-in-Kingfisher) for a use case on it. */ public static var KingfisherDidCleanDiskCache = Notification.Name.init("com.onevcat.Kingfisher.KingfisherDidCleanDiskCache") } /** Key for array of cleaned hashes in `userInfo` of `KingfisherDidCleanDiskCacheNotification`. */ public let KingfisherDiskCacheCleanedHashKey = "com.onevcat.Kingfisher.cleanedHash" /// It represents a task of retrieving image. You can call `cancel` on it to stop the process. public typealias RetrieveImageDiskTask = DispatchWorkItem /** Cache type of a cached image. - None: The image is not cached yet when retrieving it. - Memory: The image is cached in memory. - Disk: The image is cached in disk. */ public enum CacheType { case none, memory, disk public var cached: Bool { switch self { case .memory, .disk: return true case .none: return false } } } /// `ImageCache` represents both the memory and disk cache system of Kingfisher. /// While a default image cache object will be used if you prefer the extension methods of Kingfisher, /// you can create your own cache object and configure it as your need. You could use an `ImageCache` /// object to manipulate memory and disk cache for Kingfisher. open class ImageCache { //Memory fileprivate let memoryCache = NSCache() /// The largest cache cost of memory cache. The total cost is pixel count of /// all cached images in memory. /// Default is unlimited. Memory cache will be purged automatically when a /// memory warning notification is received. open var maxMemoryCost: UInt = 0 { didSet { self.memoryCache.totalCostLimit = Int(maxMemoryCost) } } //Disk fileprivate let ioQueue: DispatchQueue fileprivate var fileManager: FileManager! ///The disk cache location. open let diskCachePath: String /// The default file extension appended to cached files. open var pathExtension: String? /// The longest time duration in second of the cache being stored in disk. /// Default is 1 week (60 * 60 * 24 * 7 seconds). /// Setting this to a negative value will make the disk cache never expiring. open var maxCachePeriodInSecond: TimeInterval = 60 * 60 * 24 * 7 //Cache exists for 1 week /// The largest disk size can be taken for the cache. It is the total /// allocated size of cached files in bytes. /// Default is no limit. open var maxDiskCacheSize: UInt = 0 fileprivate let processQueue: DispatchQueue /// The default cache. public static let `default` = ImageCache(name: "default") /// Closure that defines the disk cache path from a given path and cacheName. public typealias DiskCachePathClosure = (String?, String) -> String /// The default DiskCachePathClosure public final class func defaultDiskCachePathClosure(path: String?, cacheName: String) -> String { let dstPath = path ?? NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first! return (dstPath as NSString).appendingPathComponent(cacheName) } /** Init method. Passing a name for the cache. It represents a cache folder in the memory and disk. - parameter name: Name of the cache. It will be used as the memory cache name and the disk cache folder name appending to the cache path. This value should not be an empty string. - parameter path: Optional - Location of cache path on disk. If `nil` is passed in (the default value), the `.cachesDirectory` in of your app will be used. - parameter diskCachePathClosure: Closure that takes in an optional initial path string and generates the final disk cache path. You could use it to fully customize your cache path. - returns: The cache object. */ public init(name: String, path: String? = nil, diskCachePathClosure: DiskCachePathClosure = ImageCache.defaultDiskCachePathClosure) { if name.isEmpty { fatalError("[Kingfisher] You should specify a name for the cache. A cache with empty name is not permitted.") } let cacheName = "com.onevcat.Kingfisher.ImageCache.\(name)" memoryCache.name = cacheName diskCachePath = diskCachePathClosure(path, cacheName) let ioQueueName = "com.onevcat.Kingfisher.ImageCache.ioQueue.\(name)" ioQueue = DispatchQueue(label: ioQueueName) let processQueueName = "com.onevcat.Kingfisher.ImageCache.processQueue.\(name)" processQueue = DispatchQueue(label: processQueueName, attributes: .concurrent) ioQueue.sync { fileManager = FileManager() } #if !os(macOS) && !os(watchOS) NotificationCenter.default.addObserver( self, selector: #selector(clearMemoryCache), name: .UIApplicationDidReceiveMemoryWarning, object: nil) NotificationCenter.default.addObserver( self, selector: #selector(cleanExpiredDiskCache), name: .UIApplicationWillTerminate, object: nil) NotificationCenter.default.addObserver( self, selector: #selector(backgroundCleanExpiredDiskCache), name: .UIApplicationDidEnterBackground, object: nil) #endif } deinit { NotificationCenter.default.removeObserver(self) } // MARK: - Store & Remove /** Store an image to cache. It will be saved to both memory and disk. It is an async operation. - parameter image: The image to be stored. - parameter original: The original data of the image. Kingfisher will use it to check the format of the image and optimize cache size on disk. If `nil` is supplied, the image data will be saved as a normalized PNG file. It is strongly suggested to supply it whenever possible, to get a better performance and disk usage. - parameter key: Key for the image. - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it. This identifier will be used to generate a corresponding key for the combination of `key` and processor. - parameter toDisk: Whether this image should be cached to disk or not. If false, the image will be only cached in memory. - parameter completionHandler: Called when store operation completes. */ open func store(_ image: Image, original: Data? = nil, forKey key: String, processorIdentifier identifier: String = "", cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default, toDisk: Bool = true, completionHandler: (() -> Void)? = nil) { let computedKey = key.computedKey(with: identifier) memoryCache.setObject(image, forKey: computedKey as NSString, cost: image.kf.imageCost) func callHandlerInMainQueue() { if let handler = completionHandler { DispatchQueue.main.async { handler() } } } if toDisk { ioQueue.async { if let data = serializer.data(with: image, original: original) { if !self.fileManager.fileExists(atPath: self.diskCachePath) { do { try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil) } catch _ {} } self.fileManager.createFile(atPath: self.cachePath(forComputedKey: computedKey), contents: data, attributes: nil) } callHandlerInMainQueue() } } else { callHandlerInMainQueue() } } /** Remove the image for key for the cache. It will be opted out from both memory and disk. It is an async operation. - parameter key: Key for the image. - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it. This identifier will be used to generate a corresponding key for the combination of `key` and processor. - parameter fromDisk: Whether this image should be removed from disk or not. If false, the image will be only removed from memory. - parameter completionHandler: Called when removal operation completes. */ open func removeImage(forKey key: String, processorIdentifier identifier: String = "", fromDisk: Bool = true, completionHandler: (() -> Void)? = nil) { let computedKey = key.computedKey(with: identifier) memoryCache.removeObject(forKey: computedKey as NSString) func callHandlerInMainQueue() { if let handler = completionHandler { DispatchQueue.main.async { handler() } } } if fromDisk { ioQueue.async{ do { try self.fileManager.removeItem(atPath: self.cachePath(forComputedKey: computedKey)) } catch _ {} callHandlerInMainQueue() } } else { callHandlerInMainQueue() } } // MARK: - Get data from cache /** Get an image for a key from memory or disk. - parameter key: Key for the image. - parameter options: Options of retrieving image. If you need to retrieve an image which was stored with a specified `ImageProcessor`, pass the processor in the option too. - parameter completionHandler: Called when getting operation completes with image result and cached type of this image. If there is no such key cached, the image will be `nil`. - returns: The retrieving task. */ @discardableResult open func retrieveImage(forKey key: String, options: KingfisherOptionsInfo?, completionHandler: ((Image?, CacheType) -> ())?) -> RetrieveImageDiskTask? { // No completion handler. Not start working and early return. guard let completionHandler = completionHandler else { return nil } var block: RetrieveImageDiskTask? let options = options ?? KingfisherEmptyOptionsInfo if let image = self.retrieveImageInMemoryCache(forKey: key, options: options) { options.callbackDispatchQueue.safeAsync { completionHandler(image, .memory) } } else if options.fromMemoryCacheOrRefresh { // Only allows to get images from memory cache. options.callbackDispatchQueue.safeAsync { completionHandler(nil, .none) } } else { var sSelf: ImageCache! = self block = DispatchWorkItem(block: { // Begin to load image from disk if let image = sSelf.retrieveImageInDiskCache(forKey: key, options: options) { if options.backgroundDecode { sSelf.processQueue.async { let result = image.kf.decoded sSelf.store(result, forKey: key, processorIdentifier: options.processor.identifier, cacheSerializer: options.cacheSerializer, toDisk: false, completionHandler: nil) options.callbackDispatchQueue.safeAsync { completionHandler(result, .memory) sSelf = nil } } } else { sSelf.store(image, forKey: key, processorIdentifier: options.processor.identifier, cacheSerializer: options.cacheSerializer, toDisk: false, completionHandler: nil ) options.callbackDispatchQueue.safeAsync { completionHandler(image, .disk) sSelf = nil } } } else { // No image found from either memory or disk options.callbackDispatchQueue.safeAsync { completionHandler(nil, .none) sSelf = nil } } }) sSelf.ioQueue.async(execute: block!) } return block } /** Get an image for a key from memory. - parameter key: Key for the image. - parameter options: Options of retrieving image. If you need to retrieve an image which was stored with a specified `ImageProcessor`, pass the processor in the option too. - returns: The image object if it is cached, or `nil` if there is no such key in the cache. */ open func retrieveImageInMemoryCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? { let options = options ?? KingfisherEmptyOptionsInfo let computedKey = key.computedKey(with: options.processor.identifier) return memoryCache.object(forKey: computedKey as NSString) as? Image } /** Get an image for a key from disk. - parameter key: Key for the image. - parameter options: Options of retrieving image. If you need to retrieve an image which was stored with a specified `ImageProcessor`, pass the processor in the option too. - returns: The image object if it is cached, or `nil` if there is no such key in the cache. */ open func retrieveImageInDiskCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? { let options = options ?? KingfisherEmptyOptionsInfo let computedKey = key.computedKey(with: options.processor.identifier) return diskImage(forComputedKey: computedKey, serializer: options.cacheSerializer, options: options) } // MARK: - Clear & Clean /** Clear memory cache. */ @objc public func clearMemoryCache() { memoryCache.removeAllObjects() } /** Clear disk cache. This is an async operation. - parameter completionHander: Called after the operation completes. */ open func clearDiskCache(completion handler: (()->())? = nil) { ioQueue.async { do { try self.fileManager.removeItem(atPath: self.diskCachePath) try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil) } catch _ { } if let handler = handler { DispatchQueue.main.async { handler() } } } } /** Clean expired disk cache. This is an async operation. */ @objc fileprivate func cleanExpiredDiskCache() { cleanExpiredDiskCache(completion: nil) } /** Clean expired disk cache. This is an async operation. - parameter completionHandler: Called after the operation completes. */ open func cleanExpiredDiskCache(completion handler: (()->())? = nil) { // Do things in cocurrent io queue ioQueue.async { var (URLsToDelete, diskCacheSize, cachedFiles) = self.travelCachedFiles(onlyForCacheSize: false) for fileURL in URLsToDelete { do { try self.fileManager.removeItem(at: fileURL) } catch _ { } } if self.maxDiskCacheSize > 0 && diskCacheSize > self.maxDiskCacheSize { let targetSize = self.maxDiskCacheSize / 2 // Sort files by last modify date. We want to clean from the oldest files. let sortedFiles = cachedFiles.keysSortedByValue { resourceValue1, resourceValue2 -> Bool in if let date1 = resourceValue1.contentAccessDate, let date2 = resourceValue2.contentAccessDate { return date1.compare(date2) == .orderedAscending } // Not valid date information. This should not happen. Just in case. return true } for fileURL in sortedFiles { do { try self.fileManager.removeItem(at: fileURL) } catch { } URLsToDelete.append(fileURL) if let fileSize = cachedFiles[fileURL]?.totalFileAllocatedSize { diskCacheSize -= UInt(fileSize) } if diskCacheSize < targetSize { break } } } DispatchQueue.main.async { if URLsToDelete.count != 0 { let cleanedHashes = URLsToDelete.map { $0.lastPathComponent } NotificationCenter.default.post(name: .KingfisherDidCleanDiskCache, object: self, userInfo: [KingfisherDiskCacheCleanedHashKey: cleanedHashes]) } handler?() } } } fileprivate func travelCachedFiles(onlyForCacheSize: Bool) -> (urlsToDelete: [URL], diskCacheSize: UInt, cachedFiles: [URL: URLResourceValues]) { let diskCacheURL = URL(fileURLWithPath: diskCachePath) let resourceKeys: Set = [.isDirectoryKey, .contentAccessDateKey, .totalFileAllocatedSizeKey] let expiredDate: Date? = (maxCachePeriodInSecond < 0) ? nil : Date(timeIntervalSinceNow: -maxCachePeriodInSecond) var cachedFiles = [URL: URLResourceValues]() var urlsToDelete = [URL]() var diskCacheSize: UInt = 0 for fileUrl in (try? fileManager.contentsOfDirectory(at: diskCacheURL, includingPropertiesForKeys: Array(resourceKeys), options: .skipsHiddenFiles)) ?? [] { do { let resourceValues = try fileUrl.resourceValues(forKeys: resourceKeys) // If it is a Directory. Continue to next file URL. if resourceValues.isDirectory == true { continue } // If this file is expired, add it to URLsToDelete if !onlyForCacheSize, let expiredDate = expiredDate, let lastAccessData = resourceValues.contentAccessDate, (lastAccessData as NSDate).laterDate(expiredDate) == expiredDate { urlsToDelete.append(fileUrl) continue } if let fileSize = resourceValues.totalFileAllocatedSize { diskCacheSize += UInt(fileSize) if !onlyForCacheSize { cachedFiles[fileUrl] = resourceValues } } } catch _ { } } return (urlsToDelete, diskCacheSize, cachedFiles) } #if !os(macOS) && !os(watchOS) /** Clean expired disk cache when app in background. This is an async operation. In most cases, you should not call this method explicitly. It will be called automatically when `UIApplicationDidEnterBackgroundNotification` received. */ @objc public func backgroundCleanExpiredDiskCache() { // if 'sharedApplication()' is unavailable, then return guard let sharedApplication = Kingfisher.shared else { return } func endBackgroundTask(_ task: inout UIBackgroundTaskIdentifier) { sharedApplication.endBackgroundTask(task) task = UIBackgroundTaskInvalid } var backgroundTask: UIBackgroundTaskIdentifier! backgroundTask = sharedApplication.beginBackgroundTask { endBackgroundTask(&backgroundTask!) } cleanExpiredDiskCache { endBackgroundTask(&backgroundTask!) } } #endif // MARK: - Check cache status /// Cache type for checking whether an image is cached for a key in current cache. /// /// - Parameters: /// - key: Key for the image. /// - identifier: Processor identifier which used for this image. Default is empty string. /// - Returns: A `CacheType` instance which indicates the cache status. `.none` means the image is not in cache yet. open func imageCachedType(forKey key: String, processorIdentifier identifier: String = "") -> CacheType { let computedKey = key.computedKey(with: identifier) if memoryCache.object(forKey: computedKey as NSString) != nil { return .memory } let filePath = cachePath(forComputedKey: computedKey) var diskCached = false ioQueue.sync { diskCached = fileManager.fileExists(atPath: filePath) } if diskCached { return .disk } return .none } /** Get the hash for the key. This could be used for matching files. - parameter key: The key which is used for caching. - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it. - returns: Corresponding hash. */ open func hash(forKey key: String, processorIdentifier identifier: String = "") -> String { let computedKey = key.computedKey(with: identifier) return cacheFileName(forComputedKey: computedKey) } /** Calculate the disk size taken by cache. It is the total allocated size of the cached files in bytes. - parameter completionHandler: Called with the calculated size when finishes. */ open func calculateDiskCacheSize(completion handler: @escaping ((_ size: UInt) -> ())) { ioQueue.async { let (_, diskCacheSize, _) = self.travelCachedFiles(onlyForCacheSize: true) DispatchQueue.main.async { handler(diskCacheSize) } } } /** Get the cache path for the key. It is useful for projects with UIWebView or anyone that needs access to the local file path. i.e. Replace the `` tag in your HTML. - Note: This method does not guarantee there is an image already cached in the path. It just returns the path that the image should be. You could use `isImageCached(forKey:)` method to check whether the image is cached under that key. */ open func cachePath(forKey key: String, processorIdentifier identifier: String = "") -> String { let computedKey = key.computedKey(with: identifier) return cachePath(forComputedKey: computedKey) } open func cachePath(forComputedKey key: String) -> String { let fileName = cacheFileName(forComputedKey: key) return (diskCachePath as NSString).appendingPathComponent(fileName) } } // MARK: - Internal Helper extension ImageCache { func diskImage(forComputedKey key: String, serializer: CacheSerializer, options: KingfisherOptionsInfo) -> Image? { if let data = diskImageData(forComputedKey: key) { return serializer.image(with: data, options: options) } else { return nil } } func diskImageData(forComputedKey key: String) -> Data? { let filePath = cachePath(forComputedKey: key) return (try? Data(contentsOf: URL(fileURLWithPath: filePath))) } func cacheFileName(forComputedKey key: String) -> String { if let ext = self.pathExtension { return (key.kf.md5 as NSString).appendingPathExtension(ext)! } return key.kf.md5 } } // MARK: - Deprecated extension ImageCache { /** * Cache result for checking whether an image is cached for a key. */ @available(*, deprecated, message: "CacheCheckResult is deprecated. Use imageCachedType(forKey:processorIdentifier:) API instead.") public struct CacheCheckResult { public let cached: Bool public let cacheType: CacheType? } /** Check whether an image is cached for a key. - parameter key: Key for the image. - returns: The check result. */ @available(*, deprecated, message: "Use imageCachedType(forKey:processorIdentifier:) instead. CacheCheckResult.none indicates not being cached.", renamed: "imageCachedType(forKey:processorIdentifier:)") open func isImageCached(forKey key: String, processorIdentifier identifier: String = "") -> CacheCheckResult { let result = imageCachedType(forKey: key, processorIdentifier: identifier) switch result { case .memory, .disk: return CacheCheckResult(cached: true, cacheType: result) case .none: return CacheCheckResult(cached: false, cacheType: nil) } } } extension Kingfisher where Base: Image { var imageCost: Int { return images == nil ? Int(size.height * size.width * scale * scale) : Int(size.height * size.width * scale * scale) * images!.count } } extension Dictionary { func keysSortedByValue(_ isOrderedBefore: (Value, Value) -> Bool) -> [Key] { return Array(self).sorted{ isOrderedBefore($0.1, $1.1) }.map{ $0.0 } } } #if !os(macOS) && !os(watchOS) // MARK: - For App Extensions extension UIApplication: KingfisherCompatible { } extension Kingfisher where Base: UIApplication { public static var shared: UIApplication? { let selector = NSSelectorFromString("sharedApplication") guard Base.responds(to: selector) else { return nil } return Base.perform(selector).takeUnretainedValue() as? UIApplication } } #endif extension String { func computedKey(with identifier: String) -> String { if identifier.isEmpty { return self } else { return appending("@\(identifier)") } } } ================================================ FILE: Pods/Kingfisher/Sources/ImageDownloader.swift ================================================ // // ImageDownloader.swift // Kingfisher // // Created by Wei Wang on 15/4/6. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif /// Progress update block of downloader. public typealias ImageDownloaderProgressBlock = DownloadProgressBlock /// Completion block of downloader. public typealias ImageDownloaderCompletionHandler = ((_ image: Image?, _ error: NSError?, _ url: URL?, _ originalData: Data?) -> ()) /// Download task. public struct RetrieveImageDownloadTask { let internalTask: URLSessionDataTask /// Downloader by which this task is intialized. public private(set) weak var ownerDownloader: ImageDownloader? /** Cancel this download task. It will trigger the completion handler with an NSURLErrorCancelled error. */ public func cancel() { ownerDownloader?.cancelDownloadingTask(self) } /// The original request URL of this download task. public var url: URL? { return internalTask.originalRequest?.url } /// The relative priority of this download task. /// It represents the `priority` property of the internal `NSURLSessionTask` of this download task. /// The value for it is between 0.0~1.0. Default priority is value of 0.5. /// See documentation on `priority` of `NSURLSessionTask` for more about it. public var priority: Float { get { return internalTask.priority } set { internalTask.priority = newValue } } } ///The code of errors which `ImageDownloader` might encountered. public enum KingfisherError: Int { /// badData: The downloaded data is not an image or the data is corrupted. case badData = 10000 /// notModified: The remote server responsed a 304 code. No image data downloaded. case notModified = 10001 /// The HTTP status code in response is not valid. If an invalid /// code error received, you could check the value under `KingfisherErrorStatusCodeKey` /// in `userInfo` to see the code. case invalidStatusCode = 10002 /// notCached: The image rquested is not in cache but .onlyFromCache is activated. case notCached = 10003 /// The URL is invalid. case invalidURL = 20000 /// The downloading task is cancelled before started. case downloadCancelledBeforeStarting = 30000 } /// Key will be used in the `userInfo` of `.invalidStatusCode` public let KingfisherErrorStatusCodeKey = "statusCode" /// Protocol of `ImageDownloader`. public protocol ImageDownloaderDelegate: class { /** Called when the `ImageDownloader` object successfully downloaded an image from specified URL. - parameter downloader: The `ImageDownloader` object finishes the downloading. - parameter image: Downloaded image. - parameter url: URL of the original request URL. - parameter response: The response object of the downloading process. */ func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) /** Called when the `ImageDownloader` object starts to download an image from specified URL. - parameter downloader: The `ImageDownloader` object starts the downloading. - parameter url: URL of the original request. - parameter response: The request object of the downloading process. */ func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?) /** Check if a received HTTP status code is valid or not. By default, a status code between 200 to 400 (excluded) is considered as valid. If an invalid code is received, the downloader will raise an .invalidStatusCode error. It has a `userInfo` which includes this statusCode and localizedString error message. - parameter code: The received HTTP status code. - parameter downloader: The `ImageDownloader` object asking for validate status code. - returns: Whether this HTTP status code is valid or not. - Note: If the default 200 to 400 valid code does not suit your need, you can implement this method to change that behavior. */ func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool /** Called when the `ImageDownloader` object successfully downloaded image data from specified URL. - parameter downloader: The `ImageDownloader` object finishes data downloading. - parameter data: Downloaded data. - parameter url: URL of the original request URL. - returns: The data from which Kingfisher should use to create an image. - Note: This callback can be used to preprocess raw image data before creation of UIImage instance (i.e. decrypting or verification). */ func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? } extension ImageDownloaderDelegate { public func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) {} public func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?) {} public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool { return (200..<400).contains(code) } public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? { return data } } /// Protocol indicates that an authentication challenge could be handled. public protocol AuthenticationChallengeResponsable: class { /** Called when an session level authentication challenge is received. This method provide a chance to handle and response to the authentication challenge before downloading could start. - parameter downloader: The downloader which receives this challenge. - parameter challenge: An object that contains the request for authentication. - parameter completionHandler: A handler that your delegate method must call. - Note: This method is a forward from `URLSessionDelegate.urlSession(:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `URLSessionDelegate`. */ func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) /** Called when an session level authentication challenge is received. This method provide a chance to handle and response to the authentication challenge before downloading could start. - parameter downloader: The downloader which receives this challenge. - parameter task: The task whose request requires authentication. - parameter challenge: An object that contains the request for authentication. - parameter completionHandler: A handler that your delegate method must call. - Note: This method is a forward from `URLSessionTaskDelegate.urlSession(:task:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `URLSessionTaskDelegate`. */ func downloader(_ downloader: ImageDownloader, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) } extension AuthenticationChallengeResponsable { func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) { let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) completionHandler(.useCredential, credential) return } } completionHandler(.performDefaultHandling, nil) } func downloader(_ downloader: ImageDownloader, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { completionHandler(.performDefaultHandling, nil) } } /// `ImageDownloader` represents a downloading manager for requesting the image with a URL from server. open class ImageDownloader { class ImageFetchLoad { var contents = [(callback: CallbackPair, options: KingfisherOptionsInfo)]() var responseData = NSMutableData() var downloadTaskCount = 0 var downloadTask: RetrieveImageDownloadTask? var cancelSemaphore: DispatchSemaphore? } // MARK: - Public property /// The duration before the download is timeout. Default is 15 seconds. open var downloadTimeout: TimeInterval = 15.0 /// A set of trusted hosts when receiving server trust challenges. A challenge with host name contained in this set will be ignored. /// You can use this set to specify the self-signed site. It only will be used if you don't specify the `authenticationChallengeResponder`. /// If `authenticationChallengeResponder` is set, this property will be ignored and the implemention of `authenticationChallengeResponder` will be used instead. open var trustedHosts: Set? /// Use this to set supply a configuration for the downloader. By default, NSURLSessionConfiguration.ephemeralSessionConfiguration() will be used. /// You could change the configuration before a downloaing task starts. A configuration without persistent storage for caches is requsted for downloader working correctly. open var sessionConfiguration = URLSessionConfiguration.ephemeral { didSet { session?.invalidateAndCancel() session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: OperationQueue.main) } } /// Whether the download requests should use pipeling or not. Default is false. open var requestsUsePipelining = false fileprivate let sessionHandler: ImageDownloaderSessionHandler fileprivate var session: URLSession? /// Delegate of this `ImageDownloader` object. See `ImageDownloaderDelegate` protocol for more. open weak var delegate: ImageDownloaderDelegate? /// A responder for authentication challenge. /// Downloader will forward the received authentication challenge for the downloading session to this responder. open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable? // MARK: - Internal property let barrierQueue: DispatchQueue let processQueue: DispatchQueue let cancelQueue: DispatchQueue typealias CallbackPair = (progressBlock: ImageDownloaderProgressBlock?, completionHandler: ImageDownloaderCompletionHandler?) var fetchLoads = [URL: ImageFetchLoad]() // MARK: - Public method /// The default downloader. public static let `default` = ImageDownloader(name: "default") /** Init a downloader with name. - parameter name: The name for the downloader. It should not be empty. - returns: The downloader object. */ public init(name: String) { if name.isEmpty { fatalError("[Kingfisher] You should specify a name for the downloader. A downloader with empty name is not permitted.") } barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent) processQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process.\(name)", attributes: .concurrent) cancelQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Cancel.\(name)") sessionHandler = ImageDownloaderSessionHandler() // Provide a default implement for challenge responder. authenticationChallengeResponder = sessionHandler session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: .main) } deinit { session?.invalidateAndCancel() } func fetchLoad(for url: URL) -> ImageFetchLoad? { var fetchLoad: ImageFetchLoad? barrierQueue.sync(flags: .barrier) { fetchLoad = fetchLoads[url] } return fetchLoad } /** Download an image with a URL and option. - parameter url: Target URL. - parameter retrieveImageTask: The task to cooporate with cache. Pass `nil` if you are not trying to use downloader and cache. - parameter options: The options could control download behavior. See `KingfisherOptionsInfo`. - parameter progressBlock: Called when the download progress updated. - parameter completionHandler: Called when the download progress finishes. - returns: A downloading task. You could call `cancel` on it to stop the downloading process. */ @discardableResult open func downloadImage(with url: URL, retrieveImageTask: RetrieveImageTask? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: ImageDownloaderProgressBlock? = nil, completionHandler: ImageDownloaderCompletionHandler? = nil) -> RetrieveImageDownloadTask? { if let retrieveImageTask = retrieveImageTask, retrieveImageTask.cancelledBeforeDownloadStarting { completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil) return nil } let timeout = self.downloadTimeout == 0.0 ? 15.0 : self.downloadTimeout // We need to set the URL as the load key. So before setup progress, we need to ask the `requestModifier` for a final URL. var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: timeout) request.httpShouldUsePipelining = requestsUsePipelining if let modifier = options?.modifier { guard let r = modifier.modified(for: request) else { completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil) return nil } request = r } // There is a possiblility that request modifier changed the url to `nil` or empty. guard let url = request.url, !url.absoluteString.isEmpty else { completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.invalidURL.rawValue, userInfo: nil), nil, nil) return nil } var downloadTask: RetrieveImageDownloadTask? setup(progressBlock: progressBlock, with: completionHandler, for: url, options: options) {(session, fetchLoad) -> Void in if fetchLoad.downloadTask == nil { let dataTask = session.dataTask(with: request) fetchLoad.downloadTask = RetrieveImageDownloadTask(internalTask: dataTask, ownerDownloader: self) dataTask.priority = options?.downloadPriority ?? URLSessionTask.defaultPriority dataTask.resume() self.delegate?.imageDownloader(self, willDownloadImageForURL: url, with: request) // Hold self while the task is executing. self.sessionHandler.downloadHolder = self } fetchLoad.downloadTaskCount += 1 downloadTask = fetchLoad.downloadTask retrieveImageTask?.downloadTask = downloadTask } return downloadTask } } // MARK: - Download method extension ImageDownloader { // A single key may have multiple callbacks. Only download once. func setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: @escaping ((URLSession, ImageFetchLoad) -> Void)) { func prepareFetchLoad() { barrierQueue.sync(flags: .barrier) { let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad() let callbackPair = (progressBlock: progressBlock, completionHandler: completionHandler) loadObjectForURL.contents.append((callbackPair, options ?? KingfisherEmptyOptionsInfo)) fetchLoads[url] = loadObjectForURL if let session = session { started(session, loadObjectForURL) } } } if let fetchLoad = fetchLoad(for: url), fetchLoad.downloadTaskCount == 0 { if fetchLoad.cancelSemaphore == nil { fetchLoad.cancelSemaphore = DispatchSemaphore(value: 0) } cancelQueue.async { _ = fetchLoad.cancelSemaphore?.wait(timeout: .distantFuture) fetchLoad.cancelSemaphore = nil prepareFetchLoad() } } else { prepareFetchLoad() } } func cancelDownloadingTask(_ task: RetrieveImageDownloadTask) { barrierQueue.sync(flags: .barrier) { if let URL = task.internalTask.originalRequest?.url, let imageFetchLoad = self.fetchLoads[URL] { imageFetchLoad.downloadTaskCount -= 1 if imageFetchLoad.downloadTaskCount == 0 { task.internalTask.cancel() } } } } } // MARK: - NSURLSessionDataDelegate /// Delegate class for `NSURLSessionTaskDelegate`. /// The session object will hold its delegate until it gets invalidated. /// If we use `ImageDownloader` as the session delegate, it will not be released. /// So we need an additional handler to break the retain cycle. // See https://github.com/onevcat/Kingfisher/issues/235 class ImageDownloaderSessionHandler: NSObject, URLSessionDataDelegate, AuthenticationChallengeResponsable { // The holder will keep downloader not released while a data task is being executed. // It will be set when the task started, and reset when the task finished. var downloadHolder: ImageDownloader? func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { guard let downloader = downloadHolder else { completionHandler(.cancel) return } if let statusCode = (response as? HTTPURLResponse)?.statusCode, let url = dataTask.originalRequest?.url, !(downloader.delegate ?? downloader).isValidStatusCode(statusCode, for: downloader) { let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.invalidStatusCode.rawValue, userInfo: [KingfisherErrorStatusCodeKey: statusCode, NSLocalizedDescriptionKey: HTTPURLResponse.localizedString(forStatusCode: statusCode)]) callCompletionHandlerFailure(error: error, url: url) } completionHandler(.allow) } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { guard let downloader = downloadHolder else { return } if let url = dataTask.originalRequest?.url, let fetchLoad = downloader.fetchLoad(for: url) { fetchLoad.responseData.append(data) if let expectedLength = dataTask.response?.expectedContentLength { for content in fetchLoad.contents { DispatchQueue.main.async { content.callback.progressBlock?(Int64(fetchLoad.responseData.length), expectedLength) } } } } } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { guard let url = task.originalRequest?.url else { return } guard error == nil else { callCompletionHandlerFailure(error: error!, url: url) return } processImage(for: task, url: url) } /** This method is exposed since the compiler requests. Do not call it. */ func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let downloader = downloadHolder else { return } downloader.authenticationChallengeResponder?.downloader(downloader, didReceive: challenge, completionHandler: completionHandler) } func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let downloader = downloadHolder else { return } downloader.authenticationChallengeResponder?.downloader(downloader, task: task, didReceive: challenge, completionHandler: completionHandler) } private func cleanFetchLoad(for url: URL) { guard let downloader = downloadHolder else { return } downloader.barrierQueue.sync(flags: .barrier) { downloader.fetchLoads.removeValue(forKey: url) if downloader.fetchLoads.isEmpty { downloadHolder = nil } } } private func callCompletionHandlerFailure(error: Error, url: URL) { guard let downloader = downloadHolder, let fetchLoad = downloader.fetchLoad(for: url) else { return } // We need to clean the fetch load first, before actually calling completion handler. cleanFetchLoad(for: url) var leftSignal: Int repeat { leftSignal = fetchLoad.cancelSemaphore?.signal() ?? 0 } while leftSignal != 0 for content in fetchLoad.contents { content.options.callbackDispatchQueue.safeAsync { content.callback.completionHandler?(nil, error as NSError, url, nil) } } } private func processImage(for task: URLSessionTask, url: URL) { guard let downloader = downloadHolder else { return } // We are on main queue when receiving this. downloader.processQueue.async { guard let fetchLoad = downloader.fetchLoad(for: url) else { return } self.cleanFetchLoad(for: url) let data: Data? let fetchedData = fetchLoad.responseData as Data if let delegate = downloader.delegate { data = delegate.imageDownloader(downloader, didDownload: fetchedData, for: url) } else { data = fetchedData } // Cache the processed images. So we do not need to re-process the image if using the same processor. // Key is the identifier of processor. var imageCache: [String: Image] = [:] for content in fetchLoad.contents { let options = content.options let completionHandler = content.callback.completionHandler let callbackQueue = options.callbackDispatchQueue let processor = options.processor var image = imageCache[processor.identifier] if let data = data, image == nil { image = processor.process(item: .data(data), options: options) // Add the processed image to cache. // If `image` is nil, nothing will happen (since the key is not existing before). imageCache[processor.identifier] = image } if let image = image { downloader.delegate?.imageDownloader(downloader, didDownload: image, for: url, with: task.response) if options.backgroundDecode { let decodedImage = image.kf.decoded callbackQueue.safeAsync { completionHandler?(decodedImage, nil, url, data) } } else { callbackQueue.safeAsync { completionHandler?(image, nil, url, data) } } } else { if let res = task.response as? HTTPURLResponse , res.statusCode == 304 { let notModified = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notModified.rawValue, userInfo: nil) completionHandler?(nil, notModified, url, nil) continue } let badData = NSError(domain: KingfisherErrorDomain, code: KingfisherError.badData.rawValue, userInfo: nil) callbackQueue.safeAsync { completionHandler?(nil, badData, url, nil) } } } } } } // Placeholder. For retrieving extension methods of ImageDownloaderDelegate extension ImageDownloader: ImageDownloaderDelegate {} ================================================ FILE: Pods/Kingfisher/Sources/ImagePrefetcher.swift ================================================ // // ImagePrefetcher.swift // Kingfisher // // Created by Claire Knight on 24/02/2016 // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif /// Progress update block of prefetcher. /// /// - `skippedResources`: An array of resources that are already cached before the prefetching starting. /// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. /// - `completedResources`: An array of resources that are downloaded and cached successfully. public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) /// Completion block of prefetcher. /// /// - `skippedResources`: An array of resources that are already cached before the prefetching starting. /// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. /// - `completedResources`: An array of resources that are downloaded and cached successfully. public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) /// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them. /// This is useful when you know a list of image resources and want to download them before showing. public class ImagePrefetcher { /// The maximum concurrent downloads to use when prefetching images. Default is 5. public var maxConcurrentDownloads = 5 private let prefetchResources: [Resource] private let optionsInfo: KingfisherOptionsInfo private var progressBlock: PrefetcherProgressBlock? private var completionHandler: PrefetcherCompletionHandler? private var tasks = [URL: RetrieveImageDownloadTask]() private var pendingResources: ArraySlice private var skippedResources = [Resource]() private var completedResources = [Resource]() private var failedResources = [Resource]() private var stopped = false // The created manager used for prefetch. We will use the helper method in manager. private let manager: KingfisherManager private var finished: Bool { return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty } /** Init an image prefetcher with an array of URLs. The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. The images already cached will be skipped without downloading again. - parameter urls: The URLs which should be prefetched. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. - parameter completionHandler: Called when the whole prefetching process finished. - returns: An `ImagePrefetcher` object. - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. */ public convenience init(urls: [URL], options: KingfisherOptionsInfo? = nil, progressBlock: PrefetcherProgressBlock? = nil, completionHandler: PrefetcherCompletionHandler? = nil) { let resources: [Resource] = urls.map { $0 } self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler) } /** Init an image prefetcher with an array of resources. The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable. After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. The images already cached will be skipped without downloading again. - parameter resources: The resources which should be prefetched. See `Resource` type for more. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. - parameter completionHandler: Called when the whole prefetching process finished. - returns: An `ImagePrefetcher` object. - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. */ public init(resources: [Resource], options: KingfisherOptionsInfo? = nil, progressBlock: PrefetcherProgressBlock? = nil, completionHandler: PrefetcherCompletionHandler? = nil) { prefetchResources = resources pendingResources = ArraySlice(resources) // We want all callbacks from main queue, so we ignore the call back queue in options let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo let cache = self.optionsInfo.targetCache let downloader = self.optionsInfo.downloader manager = KingfisherManager(downloader: downloader, cache: cache) self.progressBlock = progressBlock self.completionHandler = completionHandler } /** Start to download the resources and cache them. This can be useful for background downloading of assets that are required for later use in an app. This code will not try and update any UI with the results of the process. */ public func start() { // Since we want to handle the resources cancellation in main thread only. DispatchQueue.main.safeAsync { guard !self.stopped else { assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.") self.handleComplete() return } guard self.maxConcurrentDownloads > 0 else { assertionFailure("There should be concurrent downloads value should be at least 1.") self.handleComplete() return } guard self.prefetchResources.count > 0 else { self.handleComplete() return } let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads) for _ in 0 ..< initialConcurentDownloads { if let resource = self.pendingResources.popFirst() { self.startPrefetching(resource) } } } } /** Stop current downloading progress, and cancel any future prefetching activity that might be occuring. */ public func stop() { DispatchQueue.main.safeAsync { if self.finished { return } self.stopped = true self.tasks.values.forEach { $0.cancel() } } } func downloadAndCache(_ resource: Resource) { let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> () in self.tasks.removeValue(forKey: resource.downloadURL) if let _ = error { self.failedResources.append(resource) } else { self.completedResources.append(resource) } self.reportProgress() if self.stopped { if self.tasks.isEmpty { self.failedResources.append(contentsOf: self.pendingResources) self.handleComplete() } } else { self.reportCompletionOrStartNext() } } let downloadTask = manager.downloadAndCacheImage( with: resource.downloadURL, forKey: resource.cacheKey, retrieveImageTask: RetrieveImageTask(), progressBlock: nil, completionHandler: downloadTaskCompletionHandler, options: optionsInfo) if let downloadTask = downloadTask { tasks[resource.downloadURL] = downloadTask } } func append(cached resource: Resource) { skippedResources.append(resource) reportProgress() reportCompletionOrStartNext() } func startPrefetching(_ resource: Resource) { if optionsInfo.forceRefresh { downloadAndCache(resource) } else { let alreadyInCache = manager.cache.imageCachedType(forKey: resource.cacheKey, processorIdentifier: optionsInfo.processor.identifier).cached if alreadyInCache { append(cached: resource) } else { downloadAndCache(resource) } } } func reportProgress() { progressBlock?(skippedResources, failedResources, completedResources) } func reportCompletionOrStartNext() { if let resource = pendingResources.popFirst() { startPrefetching(resource) } else { guard tasks.isEmpty else { return } handleComplete() } } func handleComplete() { completionHandler?(skippedResources, failedResources, completedResources) completionHandler = nil progressBlock = nil } } ================================================ FILE: Pods/Kingfisher/Sources/ImageProcessor.swift ================================================ // // ImageProcessor.swift // Kingfisher // // Created by Wei Wang on 2016/08/26. // // Copyright (c) 2017 Wei Wang // // 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. import Foundation import CoreGraphics /// The item which could be processed by an `ImageProcessor` /// /// - image: Input image /// - data: Input data public enum ImageProcessItem { case image(Image) case data(Data) } /// An `ImageProcessor` would be used to convert some downloaded data to an image. public protocol ImageProcessor { /// Identifier of the processor. It will be used to identify the processor when /// caching and retriving an image. You might want to make sure that processors with /// same properties/functionality have the same identifiers, so correct processed images /// could be retrived with proper key. /// /// - Note: Do not supply an empty string for a customized processor, which is already taken by /// the `DefaultImageProcessor`. It is recommended to use a reverse domain name notation /// string of your own for the identifier. var identifier: String { get } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: The return value will be `nil` if processing failed while converting data to image. /// If input item is already an image and there is any errors in processing, the input /// image itself will be returned. /// - Note: Most processor only supports CG-based images. /// watchOS is not supported for processers containing filter, the input image will be returned directly on watchOS. func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? } typealias ProcessorImp = ((ImageProcessItem, KingfisherOptionsInfo) -> Image?) public extension ImageProcessor { /// Append an `ImageProcessor` to another. The identifier of the new `ImageProcessor` /// will be "\(self.identifier)|>\(another.identifier)". /// /// - parameter another: An `ImageProcessor` you want to append to `self`. /// /// - returns: The new `ImageProcessor` will process the image in the order /// of the two processors concatenated. public func append(another: ImageProcessor) -> ImageProcessor { let newIdentifier = identifier.appending("|>\(another.identifier)") return GeneralProcessor(identifier: newIdentifier) { item, options in if let image = self.process(item: item, options: options) { return another.process(item: .image(image), options: options) } else { return nil } } } } func ==(left: ImageProcessor, right: ImageProcessor) -> Bool { return left.identifier == right.identifier } func !=(left: ImageProcessor, right: ImageProcessor) -> Bool { return !(left == right) } fileprivate struct GeneralProcessor: ImageProcessor { let identifier: String let p: ProcessorImp func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { return p(item, options) } } /// The default processor. It convert the input data to a valid image. /// Images of .PNG, .JPEG and .GIF format are supported. /// If an image is given, `DefaultImageProcessor` will do nothing on it and just return that image. public struct DefaultImageProcessor: ImageProcessor { /// A default `DefaultImageProcessor` could be used across. public static let `default` = DefaultImageProcessor() /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier = "" /// Initialize a `DefaultImageProcessor` public init() {} /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): return image case .data(let data): return Kingfisher.image( data: data, scale: options.scaleFactor, preloadAllAnimationData: options.preloadAllAnimationData, onlyFirstFrame: options.onlyLoadFirstFrame) } } } public struct RectCorner: OptionSet { public let rawValue: Int public static let topLeft = RectCorner(rawValue: 1 << 0) public static let topRight = RectCorner(rawValue: 1 << 1) public static let bottomLeft = RectCorner(rawValue: 1 << 2) public static let bottomRight = RectCorner(rawValue: 1 << 3) public static let all: RectCorner = [.topLeft, .topRight, .bottomLeft, .bottomRight] public init(rawValue: Int) { self.rawValue = rawValue } var cornerIdentifier: String { if self == .all { return "" } return "_corner(\(rawValue))" } } /// Processor for making round corner images. Only CG-based images are supported in macOS, /// if a non-CG image passed in, the processor will do nothing. public struct RoundCornerImageProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier: String /// Corner radius will be applied in processing. public let cornerRadius: CGFloat /// The target corners which will be applied rounding. public let roundingCorners: RectCorner /// Target size of output image should be. If `nil`, the image will keep its original size after processing. public let targetSize: CGSize? /// Background color of the output image. If `nil`, it will stay transparent. public let backgroundColor: Color? /// Initialize a `RoundCornerImageProcessor` /// /// - parameter cornerRadius: Corner radius will be applied in processing. /// - parameter targetSize: Target size of output image should be. If `nil`, /// the image will keep its original size after processing. /// Default is `nil`. /// - parameter corners: The target corners which will be applied rounding. Default is `.all`. /// - parameter backgroundColor: Backgroud color to apply for the output image. Default is `nil`. public init(cornerRadius: CGFloat, targetSize: CGSize? = nil, roundingCorners corners: RectCorner = .all, backgroundColor: Color? = nil) { self.cornerRadius = cornerRadius self.targetSize = targetSize self.roundingCorners = corners self.backgroundColor = backgroundColor self.identifier = { var identifier = "" if let size = targetSize { identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size)\(corners.cornerIdentifier))" } else { identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)\(corners.cornerIdentifier))" } if let backgroundColor = backgroundColor { identifier += "_\(backgroundColor)" } return identifier }() } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): let size = targetSize ?? image.kf.size return image.kf.image(withRoundRadius: cornerRadius, fit: size, roundingCorners: roundingCorners, backgroundColor: backgroundColor) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Specify how a size adjusts itself to fit a target size. /// /// - none: Not scale the content. /// - aspectFit: Scale the content to fit the size of the view by maintaining the aspect ratio. /// - aspectFill: Scale the content to fill the size of the view public enum ContentMode { case none case aspectFit case aspectFill } /// Processor for resizing images. Only CG-based images are supported in macOS. public struct ResizingImageProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier: String /// The reference size for resizing operation. public let referenceSize: CGSize /// Target content mode of output image should be. /// Default to ContentMode.none public let targetContentMode: ContentMode /// Initialize a `ResizingImageProcessor`. /// /// - Parameters: /// - referenceSize: The reference size for resizing operation. /// - mode: Target content mode of output image should be. /// /// - Note: /// The instance of `ResizingImageProcessor` will follow its `mode` property /// and try to resizing the input images to fit or fill the `referenceSize`. /// That means if you are using a `mode` besides of `.none`, you may get an /// image with its size not be the same as the `referenceSize`. /// /// **Example**: With input image size: {100, 200}, /// `referenceSize`: {100, 100}, `mode`: `.aspectFit`, /// you will get an output image with size of {50, 100}, which "fit"s /// the `referenceSize`. /// /// If you need an output image exactly to be a specified size, append or use /// a `CroppingImageProcessor`. public init(referenceSize: CGSize, mode: ContentMode = .none) { self.referenceSize = referenceSize self.targetContentMode = mode if mode == .none { self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize))" } else { self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize), \(mode))" } } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): return image.kf.resize(to: referenceSize, for: targetContentMode) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Processor for adding blur effect to images. `Accelerate.framework` is used underhood for /// a better performance. A simulated Gaussian blur with specified blur radius will be applied. public struct BlurImageProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier: String /// Blur radius for the simulated Gaussian blur. public let blurRadius: CGFloat /// Initialize a `BlurImageProcessor` /// /// - parameter blurRadius: Blur radius for the simulated Gaussian blur. public init(blurRadius: CGFloat) { self.blurRadius = blurRadius self.identifier = "com.onevcat.Kingfisher.BlurImageProcessor(\(blurRadius))" } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): let radius = blurRadius * options.scaleFactor return image.kf.blurred(withRadius: radius) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Processor for adding an overlay to images. Only CG-based images are supported in macOS. public struct OverlayImageProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier: String /// Overlay color will be used to overlay the input image. public let overlay: Color /// Fraction will be used when overlay the color to image. public let fraction: CGFloat /// Initialize an `OverlayImageProcessor` /// /// - parameter overlay: Overlay color will be used to overlay the input image. /// - parameter fraction: Fraction will be used when overlay the color to image. /// From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay. public init(overlay: Color, fraction: CGFloat = 0.5) { self.overlay = overlay self.fraction = fraction self.identifier = "com.onevcat.Kingfisher.OverlayImageProcessor(\(overlay.hex)_\(fraction))" } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): return image.kf.overlaying(with: overlay, fraction: fraction) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Processor for tint images with color. Only CG-based images are supported. public struct TintImageProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier: String /// Tint color will be used to tint the input image. public let tint: Color /// Initialize a `TintImageProcessor` /// /// - parameter tint: Tint color will be used to tint the input image. public init(tint: Color) { self.tint = tint self.identifier = "com.onevcat.Kingfisher.TintImageProcessor(\(tint.hex))" } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): return image.kf.tinted(with: tint) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Processor for applying some color control to images. Only CG-based images are supported. /// watchOS is not supported. public struct ColorControlsProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier: String /// Brightness changing to image. public let brightness: CGFloat /// Contrast changing to image. public let contrast: CGFloat /// Saturation changing to image. public let saturation: CGFloat /// InputEV changing to image. public let inputEV: CGFloat /// Initialize a `ColorControlsProcessor` /// /// - parameter brightness: Brightness changing to image. /// - parameter contrast: Contrast changing to image. /// - parameter saturation: Saturation changing to image. /// - parameter inputEV: InputEV changing to image. public init(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) { self.brightness = brightness self.contrast = contrast self.saturation = saturation self.inputEV = inputEV self.identifier = "com.onevcat.Kingfisher.ColorControlsProcessor(\(brightness)_\(contrast)_\(saturation)_\(inputEV))" } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): return image.kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Processor for applying black and white effect to images. Only CG-based images are supported. /// watchOS is not supported. public struct BlackWhiteProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier = "com.onevcat.Kingfisher.BlackWhiteProcessor" /// Initialize a `BlackWhiteProcessor` public init() {} /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { return ColorControlsProcessor(brightness: 0.0, contrast: 1.0, saturation: 0.0, inputEV: 0.7) .process(item: item, options: options) } } /// Processor for cropping an image. Only CG-based images are supported. /// watchOS is not supported. public struct CroppingImageProcessor: ImageProcessor { /// Identifier of the processor. /// - Note: See documentation of `ImageProcessor` protocol for more. public let identifier: String /// Target size of output image should be. public let size: CGSize /// Anchor point from which the output size should be calculate. /// The anchor point is consisted by two values between 0.0 and 1.0. /// It indicates a related point in current image. /// See `CroppingImageProcessor.init(size:anchor:)` for more. public let anchor: CGPoint /// Initialize a `CroppingImageProcessor` /// /// - Parameters: /// - size: Target size of output image should be. /// - anchor: The anchor point from which the size should be calculated. /// Default is `CGPoint(x: 0.5, y: 0.5)`, which means the center of input image. /// - Note: /// The anchor point is consisted by two values between 0.0 and 1.0. /// It indicates a related point in current image, eg: (0.0, 0.0) for top-left /// corner, (0.5, 0.5) for center and (1.0, 1.0) for bottom-right corner. /// The `size` property of `CroppingImageProcessor` will be used along with /// `anchor` to calculate a target rectange in the size of image. /// /// The target size will be automatically calculated with a reasonable behavior. /// For example, when you have an image size of `CGSize(width: 100, height: 100)`, /// and a target size of `CGSize(width: 20, height: 20)`: /// - with a (0.0, 0.0) anchor (top-left), the crop rect will be `{0, 0, 20, 20}`; /// - with a (0.5, 0.5) anchor (center), it will be `{40, 40, 20, 20}` /// - while with a (1.0, 1.0) anchor (bottom-right), it will be `{80, 80, 20, 20}` public init(size: CGSize, anchor: CGPoint = CGPoint(x: 0.5, y: 0.5)) { self.size = size self.anchor = anchor self.identifier = "com.onevcat.Kingfisher.CroppingImageProcessor(\(size)_\(anchor))" } /// Process an input `ImageProcessItem` item to an image for this processor. /// /// - parameter item: Input item which will be processed by `self` /// - parameter options: Options when processing the item. /// /// - returns: The processed image. /// /// - Note: See documentation of `ImageProcessor` protocol for more. public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { switch item { case .image(let image): return image.kf.crop(to: size, anchorOn: anchor) case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options) } } } /// Concatenate two `ImageProcessor`s. `ImageProcessor.appen(another:)` is used internally. /// /// - parameter left: First processor. /// - parameter right: Second processor. /// /// - returns: The concatenated processor. public func >>(left: ImageProcessor, right: ImageProcessor) -> ImageProcessor { return left.append(another: right) } fileprivate extension Color { var hex: String { var r: CGFloat = 0 var g: CGFloat = 0 var b: CGFloat = 0 var a: CGFloat = 0 getRed(&r, green: &g, blue: &b, alpha: &a) let rInt = Int(r * 255) << 24 let gInt = Int(g * 255) << 16 let bInt = Int(b * 255) << 8 let aInt = Int(a * 255) let rgba = rInt | gInt | bInt | aInt return String(format:"#%08x", rgba) } } ================================================ FILE: Pods/Kingfisher/Sources/ImageTransition.swift ================================================ // // ImageTransition.swift // Kingfisher // // Created by Wei Wang on 15/9/18. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) // Not implemented for macOS and watchOS yet. import AppKit /// Image transition is not supported on macOS. public enum ImageTransition { case none var duration: TimeInterval { return 0 } } #elseif os(watchOS) import UIKit /// Image transition is not supported on watchOS. public enum ImageTransition { case none var duration: TimeInterval { return 0 } } #else import UIKit /** Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher. You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` to enable the animation transition. Apple's UIViewAnimationOptions is used under the hood. For custom transition, you should specified your own transition options, animations and comletion handler as well. */ public enum ImageTransition { /// No animation transistion. case none /// Fade in the loaded image. case fade(TimeInterval) /// Flip from left transition. case flipFromLeft(TimeInterval) /// Flip from right transition. case flipFromRight(TimeInterval) /// Flip from top transition. case flipFromTop(TimeInterval) /// Flip from bottom transition. case flipFromBottom(TimeInterval) /// Custom transition. case custom(duration: TimeInterval, options: UIViewAnimationOptions, animations: ((UIImageView, UIImage) -> Void)?, completion: ((Bool) -> Void)?) var duration: TimeInterval { switch self { case .none: return 0 case .fade(let duration): return duration case .flipFromLeft(let duration): return duration case .flipFromRight(let duration): return duration case .flipFromTop(let duration): return duration case .flipFromBottom(let duration): return duration case .custom(let duration, _, _, _): return duration } } var animationOptions: UIViewAnimationOptions { switch self { case .none: return [] case .fade(_): return .transitionCrossDissolve case .flipFromLeft(_): return .transitionFlipFromLeft case .flipFromRight(_): return .transitionFlipFromRight case .flipFromTop(_): return .transitionFlipFromTop case .flipFromBottom(_): return .transitionFlipFromBottom case .custom(_, let options, _, _): return options } } var animations: ((UIImageView, UIImage) -> Void)? { switch self { case .custom(_, _, let animations, _): return animations default: return { $0.image = $1 } } } var completion: ((Bool) -> Void)? { switch self { case .custom(_, _, _, let completion): return completion default: return nil } } } #endif ================================================ FILE: Pods/Kingfisher/Sources/ImageView+Kingfisher.swift ================================================ // // ImageView+Kingfisher.swift // Kingfisher // // Created by Wei Wang on 15/4/6. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif // MARK: - Extension methods. /** * Set image to use from web. */ extension Kingfisher where Base: ImageView { /** Set an image with a resource, a placeholder image, options, progress handler and completion handler. - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter placeholder: A placeholder image when retrieving the image at URL. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called when the image downloading progress gets updated. - parameter completionHandler: Called when the image retrieved and set. - returns: A task represents the retrieving process. - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. If `resource` is `nil`, the `placeholder` image will be set and `completionHandler` will be called with both `error` and `image` being `nil`. */ @discardableResult public func setImage(with resource: Resource?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { guard let resource = resource else { self.placeholder = placeholder setWebURL(nil) completionHandler?(nil, nil, .none, nil) return .empty } var options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo) let noImageOrPlaceholderSet = base.image == nil && self.placeholder == nil if !options.keepCurrentImageWhileLoading || noImageOrPlaceholderSet { // Always set placeholder while there is no image/placehoer yet. self.placeholder = placeholder } let maybeIndicator = indicator maybeIndicator?.startAnimatingView() setWebURL(resource.downloadURL) if base.shouldPreloadAllAnimation() { options.append(.preloadAllAnimationData) } let task = KingfisherManager.shared.retrieveImage( with: resource, options: options, progressBlock: { receivedSize, totalSize in guard resource.downloadURL == self.webURL else { return } if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, completionHandler: {[weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { maybeIndicator?.stopAnimatingView() guard let strongBase = base, imageURL == self.webURL else { completionHandler?(image, error, cacheType, imageURL) return } self.setImageTask(nil) guard let image = image else { completionHandler?(nil, error, cacheType, imageURL) return } guard let transitionItem = options.lastMatchIgnoringAssociatedValue(.transition(.none)), case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else { self.placeholder = nil strongBase.image = image completionHandler?(image, error, cacheType, imageURL) return } #if !os(macOS) UIView.transition(with: strongBase, duration: 0.0, options: [], animations: { maybeIndicator?.stopAnimatingView() }, completion: { _ in self.placeholder = nil UIView.transition(with: strongBase, duration: transition.duration, options: [transition.animationOptions, .allowUserInteraction], animations: { // Set image property in the animation. transition.animations?(strongBase, image) }, completion: { finished in transition.completion?(finished) completionHandler?(image, error, cacheType, imageURL) }) }) #endif } }) setImageTask(task) return task } /** Cancel the image download task bounded to the image view if it is running. Nothing will happen if the downloading has already finished. */ public func cancelDownloadTask() { imageTask?.cancel() } } // MARK: - Associated Object private var lastURLKey: Void? private var indicatorKey: Void? private var indicatorTypeKey: Void? private var placeholderKey: Void? private var imageTaskKey: Void? extension Kingfisher where Base: ImageView { /// Get the image URL binded to this image view. public var webURL: URL? { return objc_getAssociatedObject(base, &lastURLKey) as? URL } fileprivate func setWebURL(_ url: URL?) { objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } /// Holds which indicator type is going to be used. /// Default is .none, means no indicator will be shown. public var indicatorType: IndicatorType { get { let indicator = (objc_getAssociatedObject(base, &indicatorTypeKey) as? Box)?.value return indicator ?? .none } set { switch newValue { case .none: indicator = nil case .activity: indicator = ActivityIndicator() case .image(let data): indicator = ImageIndicator(imageData: data) case .custom(let anIndicator): indicator = anIndicator } objc_setAssociatedObject(base, &indicatorTypeKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } /// Holds any type that conforms to the protocol `Indicator`. /// The protocol `Indicator` has a `view` property that will be shown when loading an image. /// It will be `nil` if `indicatorType` is `.none`. public fileprivate(set) var indicator: Indicator? { get { return (objc_getAssociatedObject(base, &indicatorKey) as? Box)?.value } set { // Remove previous if let previousIndicator = indicator { previousIndicator.view.removeFromSuperview() } // Add new if var newIndicator = newValue { // Set default indicator frame if the view's frame not set. if newIndicator.view.frame == .zero { newIndicator.view.frame = base.frame } newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY) newIndicator.view.isHidden = true base.addSubview(newIndicator.view) } // Save in associated object objc_setAssociatedObject(base, &indicatorKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } fileprivate var imageTask: RetrieveImageTask? { return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask } fileprivate func setImageTask(_ task: RetrieveImageTask?) { objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } public fileprivate(set) var placeholder: Placeholder? { get { return (objc_getAssociatedObject(base, &placeholderKey) as? Box)?.value } set { if let previousPlaceholder = placeholder { previousPlaceholder.remove(from: base) } if let newPlaceholder = newValue { newPlaceholder.add(to: base) } else { base.image = nil } objc_setAssociatedObject(base, &placeholderKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } @objc extension ImageView { func shouldPreloadAllAnimation() -> Bool { return true } } ================================================ FILE: Pods/Kingfisher/Sources/Indicator.swift ================================================ // // Indicator.swift // Kingfisher // // Created by João D. Moreira on 30/08/16. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif #if os(macOS) public typealias IndicatorView = NSView #else public typealias IndicatorView = UIView #endif public enum IndicatorType { /// No indicator. case none /// Use system activity indicator. case activity /// Use an image as indicator. GIF is supported. case image(imageData: Data) /// Use a custom indicator, which conforms to the `Indicator` protocol. case custom(indicator: Indicator) } // MARK: - Indicator Protocol public protocol Indicator { func startAnimatingView() func stopAnimatingView() var viewCenter: CGPoint { get set } var view: IndicatorView { get } } extension Indicator { #if os(macOS) public var viewCenter: CGPoint { get { let frame = view.frame return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 ) } set { let frame = view.frame let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, y: newValue.y - frame.size.height / 2.0, width: frame.size.width, height: frame.size.height) view.frame = newFrame } } #else public var viewCenter: CGPoint { get { return view.center } set { view.center = newValue } } #endif } // MARK: - ActivityIndicator // Displays a NSProgressIndicator / UIActivityIndicatorView class ActivityIndicator: Indicator { #if os(macOS) private let activityIndicatorView: NSProgressIndicator #else private let activityIndicatorView: UIActivityIndicatorView #endif private var animatingCount = 0 var view: IndicatorView { return activityIndicatorView } func startAnimatingView() { animatingCount += 1 // Alrady animating if animatingCount == 1 { #if os(macOS) activityIndicatorView.startAnimation(nil) #else activityIndicatorView.startAnimating() #endif activityIndicatorView.isHidden = false } } func stopAnimatingView() { animatingCount = max(animatingCount - 1, 0) if animatingCount == 0 { #if os(macOS) activityIndicatorView.stopAnimation(nil) #else activityIndicatorView.stopAnimating() #endif activityIndicatorView.isHidden = true } } init() { #if os(macOS) activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) activityIndicatorView.controlSize = .small activityIndicatorView.style = .spinning #else #if os(tvOS) let indicatorStyle = UIActivityIndicatorViewStyle.white #else let indicatorStyle = UIActivityIndicatorViewStyle.gray #endif activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle) activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin] #endif } } // MARK: - ImageIndicator // Displays an ImageView. Supports gif class ImageIndicator: Indicator { private let animatedImageIndicatorView: ImageView var view: IndicatorView { return animatedImageIndicatorView } init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) { var options = options // Use normal image view to show animations, so we need to preload all animation data. if !options.preloadAllAnimationData { options.append(.preloadAllAnimationData) } guard let image = processor.process(item: .data(data), options: options) else { return nil } animatedImageIndicatorView = ImageView() animatedImageIndicatorView.image = image animatedImageIndicatorView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) #if os(macOS) // Need for gif to animate on macOS self.animatedImageIndicatorView.imageScaling = .scaleNone self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true #else animatedImageIndicatorView.contentMode = .center animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin] #endif } func startAnimatingView() { #if os(macOS) animatedImageIndicatorView.animates = true #else animatedImageIndicatorView.startAnimating() #endif animatedImageIndicatorView.isHidden = false } func stopAnimatingView() { #if os(macOS) animatedImageIndicatorView.animates = false #else animatedImageIndicatorView.stopAnimating() #endif animatedImageIndicatorView.isHidden = true } } ================================================ FILE: Pods/Kingfisher/Sources/Kingfisher.h ================================================ // // Kingfisher.h // Kingfisher // // Created by Wei Wang on 15/4/6. // // Copyright (c) 2017 Wei Wang // // 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. #import //! Project version number for Kingfisher. FOUNDATION_EXPORT double KingfisherVersionNumber; //! Project version string for Kingfisher. FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import ================================================ FILE: Pods/Kingfisher/Sources/Kingfisher.swift ================================================ // // Kingfisher.swift // Kingfisher // // Created by Wei Wang on 16/9/14. // // Copyright (c) 2017 Wei Wang // // 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. import Foundation import ImageIO #if os(macOS) import AppKit public typealias Image = NSImage public typealias View = NSView public typealias Color = NSColor public typealias ImageView = NSImageView public typealias Button = NSButton #else import UIKit public typealias Image = UIImage public typealias Color = UIColor #if !os(watchOS) public typealias ImageView = UIImageView public typealias View = UIView public typealias Button = UIButton #endif #endif public final class Kingfisher { public let base: Base public init(_ base: Base) { self.base = base } } /** A type that has Kingfisher extensions. */ public protocol KingfisherCompatible { associatedtype CompatibleType var kf: CompatibleType { get } } public extension KingfisherCompatible { public var kf: Kingfisher { get { return Kingfisher(self) } } } extension Image: KingfisherCompatible { } #if !os(watchOS) extension ImageView: KingfisherCompatible { } extension Button: KingfisherCompatible { } #endif ================================================ FILE: Pods/Kingfisher/Sources/KingfisherManager.swift ================================================ // // KingfisherManager.swift // Kingfisher // // Created by Wei Wang on 15/4/6. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> ()) public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> ()) /// RetrieveImageTask represents a task of image retrieving process. /// It contains an async task of getting image from disk and from network. public class RetrieveImageTask { public static let empty = RetrieveImageTask() // If task is canceled before the download task started (which means the `downloadTask` is nil), // the download task should not begin. var cancelledBeforeDownloadStarting: Bool = false /// The network retrieve task in this image task. public var downloadTask: RetrieveImageDownloadTask? /** Cancel current task. If this task is already done, do nothing. */ public func cancel() { if let downloadTask = downloadTask { downloadTask.cancel() } else { cancelledBeforeDownloadStarting = true } } } /// Error domain of Kingfisher public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error" /// Main manager class of Kingfisher. It connects Kingfisher downloader and cache. /// You can use this class to retrieve an image via a specified URL from web or cache. public class KingfisherManager { /// Shared manager used by the extensions across Kingfisher. public static let shared = KingfisherManager() /// Cache used by this manager public var cache: ImageCache /// Downloader used by this manager public var downloader: ImageDownloader /// Default options used by the manager. This option will be used in /// Kingfisher manager related methods, including all image view and /// button extension methods. You can also passing the options per image by /// sending an `options` parameter to Kingfisher's APIs, the per image option /// will overwrite the default ones if exist. /// /// - Note: This option will not be applied to independent using of `ImageDownloader` or `ImageCache`. public var defaultOptions = KingfisherEmptyOptionsInfo var currentDefaultOptions: KingfisherOptionsInfo { return [.downloader(downloader), .targetCache(cache)] + defaultOptions } convenience init() { self.init(downloader: .default, cache: .default) } init(downloader: ImageDownloader, cache: ImageCache) { self.downloader = downloader self.cache = cache } /** Get an image with resource. If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`. These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI. - parameter completionHandler: Called when the whole retrieving process finished. - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. */ @discardableResult public func retrieveImage(with resource: Resource, options: KingfisherOptionsInfo?, progressBlock: DownloadProgressBlock?, completionHandler: CompletionHandler?) -> RetrieveImageTask { let task = RetrieveImageTask() let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo) if options.forceRefresh { _ = downloadAndCacheImage( with: resource.downloadURL, forKey: resource.cacheKey, retrieveImageTask: task, progressBlock: progressBlock, completionHandler: completionHandler, options: options) } else { tryToRetrieveImageFromCache( forKey: resource.cacheKey, with: resource.downloadURL, retrieveImageTask: task, progressBlock: progressBlock, completionHandler: completionHandler, options: options) } return task } @discardableResult func downloadAndCacheImage(with url: URL, forKey key: String, retrieveImageTask: RetrieveImageTask, progressBlock: DownloadProgressBlock?, completionHandler: CompletionHandler?, options: KingfisherOptionsInfo) -> RetrieveImageDownloadTask? { let downloader = options.downloader return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options, progressBlock: { receivedSize, totalSize in progressBlock?(receivedSize, totalSize) }, completionHandler: { image, error, imageURL, originalData in let targetCache = options.targetCache if let error = error, error.code == KingfisherError.notModified.rawValue { // Not modified. Try to find the image from cache. // (The image should be in cache. It should be guaranteed by the framework users.) targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> () in completionHandler?(cacheImage, nil, cacheType, url) }) return } if let image = image, let originalData = originalData { targetCache.store(image, original: originalData, forKey: key, processorIdentifier:options.processor.identifier, cacheSerializer: options.cacheSerializer, toDisk: !options.cacheMemoryOnly, completionHandler: nil) if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default { let originalCache = options.originalCache let defaultProcessor = DefaultImageProcessor.default if let originalImage = defaultProcessor.process(item: .data(originalData), options: options) { originalCache.store(originalImage, original: originalData, forKey: key, processorIdentifier: defaultProcessor.identifier, cacheSerializer: options.cacheSerializer, toDisk: !options.cacheMemoryOnly, completionHandler: nil) } } } completionHandler?(image, error, .none, url) }) } func tryToRetrieveImageFromCache(forKey key: String, with url: URL, retrieveImageTask: RetrieveImageTask, progressBlock: DownloadProgressBlock?, completionHandler: CompletionHandler?, options: KingfisherOptionsInfo) { let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in completionHandler?(image, error, cacheType, imageURL) } func handleNoCache() { if options.onlyFromCache { let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil) diskTaskCompletionHandler(nil, error, .none, url) return } self.downloadAndCacheImage( with: url, forKey: key, retrieveImageTask: retrieveImageTask, progressBlock: progressBlock, completionHandler: diskTaskCompletionHandler, options: options) } let targetCache = options.targetCache // First, try to get the exactly image from cache targetCache.retrieveImage(forKey: key, options: options) { image, cacheType in // If found, we could finish now. if image != nil { diskTaskCompletionHandler(image, nil, cacheType, url) return } // If not found, and we are using a default processor, download it! let processor = options.processor guard processor != DefaultImageProcessor.default else { handleNoCache() return } // If processor is not the default one, we have a chance to check whether // the original image is already in cache. let originalCache = options.originalCache let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(processor)) originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { image, cacheType in // If we found the original image, there is no need to download it again. // We could just apply processor to it now. guard let image = image else { handleNoCache() return } guard let processedImage = processor.process(item: .image(image), options: options) else { diskTaskCompletionHandler(nil, nil, .none, url) return } targetCache.store(processedImage, original: nil, forKey: key, processorIdentifier:options.processor.identifier, cacheSerializer: options.cacheSerializer, toDisk: !options.cacheMemoryOnly, completionHandler: nil) diskTaskCompletionHandler(processedImage, nil, .none, url) } } } } ================================================ FILE: Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift ================================================ // // KingfisherOptionsInfo.swift // Kingfisher // // Created by Wei Wang on 15/4/23. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif /** * KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher. */ public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem] let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]() /** Items could be added into KingfisherOptionsInfo. */ public enum KingfisherOptionsInfoItem { /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified /// cache object when handling related operations, including trying to retrieve the cached images and store /// the downloaded image to it. case targetCache(ImageCache) /// Cache for storing and retrieving original image. /// Preferred prior to targetCache for storing and retrieving original images if specified. /// Only used if a non-default image processor is involved. case originalCache(ImageCache) /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this /// downloader to download the images. case downloader(ImageDownloader) /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when /// the image being retrieved from cache, set `ForceTransition` as well. case transition(ImageTransition) /// Associated `Float` value will be set as the priority of image download task. The value for it should be /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used. case downloadPriority(Float) /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource. case forceRefresh /// If set, `Kingfisher` will try to retrieve the image from memory cache first. If the image is not in memory /// cache, then it will ignore the disk cache but download the image again from network. This is useful when /// you want to display a changeable image behind the same url, while avoiding download it again and again. case fromMemoryCacheOrRefresh /// If set, setting the image to an image view will happen with transition even when retrieved from cache. /// See `Transition` option for more. case forceTransition /// If set, `Kingfisher` will only cache the value in memory but not in disk. case cacheMemoryOnly /// If set, `Kingfisher` will only try to retrieve the image from cache not from network. case onlyFromCache /// Decode the image in background thread before using. case backgroundDecode /// The associated value of this member will be used as the target queue of dispatch callbacks when /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks. case callbackDispatchQueue(DispatchQueue?) /// The associated value of this member will be used as the scale factor when converting retrieved data to an image. /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing /// with 2x or 3x retina images. case scaleFactor(CGFloat) /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use /// corresponding image view type instead of setting this option. case preloadAllAnimationData /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent. /// This is the last chance you can modify the request. You can modify the request for some customizing purpose, /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request /// will be sent without any modification by default. case requestModifier(ImageDownloadRequestModifier) /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image /// and/or apply some filter on it. If a cache is connected to the downloader (it happenes when you are using /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the /// image view. `DefaultImageProcessor.default` will be used by default. case processor(ImageProcessor) /// Supply an `CacheSerializer` to convert some data to an image object for /// retrieving from disk cache or vice versa for storing to disk cache. /// `DefaultCacheSerializer.default` will be used by default. case cacheSerializer(CacheSerializer) /// Keep the existing image while setting another image to an image view. /// By setting this option, the placeholder image parameter of imageview extension method /// will be ignored and the current image will be kept while loading or downloading the new image. case keepCurrentImageWhileLoading /// If set, Kingfisher will only load the first frame from a animated image data file as a single image. /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a /// static preview of the first frame from a animated image. /// This option will be ignored if the target image is not animated image data. case onlyLoadFirstFrame /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both /// the final result and original image. Kingfisher will have a chance to use /// the original image when another processor is applied to the same resouce, /// instead of downloading it again. case cacheOriginalImage } precedencegroup ItemComparisonPrecedence { associativity: none higherThan: LogicalConjunctionPrecedence } infix operator <== : ItemComparisonPrecedence // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values. func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool { switch (lhs, rhs) { case (.targetCache(_), .targetCache(_)): return true case (.originalCache(_), .originalCache(_)): return true case (.downloader(_), .downloader(_)): return true case (.transition(_), .transition(_)): return true case (.downloadPriority(_), .downloadPriority(_)): return true case (.forceRefresh, .forceRefresh): return true case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true case (.forceTransition, .forceTransition): return true case (.cacheMemoryOnly, .cacheMemoryOnly): return true case (.onlyFromCache, .onlyFromCache): return true case (.backgroundDecode, .backgroundDecode): return true case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true case (.scaleFactor(_), .scaleFactor(_)): return true case (.preloadAllAnimationData, .preloadAllAnimationData): return true case (.requestModifier(_), .requestModifier(_)): return true case (.processor(_), .processor(_)): return true case (.cacheSerializer(_), .cacheSerializer(_)): return true case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true case (.cacheOriginalImage, .cacheOriginalImage): return true default: return false } } extension Collection where Iterator.Element == KingfisherOptionsInfoItem { func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? { return reversed().first { $0 <== target } } func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { return filter { !($0 <== target) } } } public extension Collection where Iterator.Element == KingfisherOptionsInfoItem { /// The target `ImageCache` which is used. public var targetCache: ImageCache { if let item = lastMatchIgnoringAssociatedValue(.targetCache(.default)), case .targetCache(let cache) = item { return cache } return ImageCache.default } /// The original `ImageCache` which is used. public var originalCache: ImageCache { if let item = lastMatchIgnoringAssociatedValue(.originalCache(.default)), case .originalCache(let cache) = item { return cache } return targetCache } /// The `ImageDownloader` which is specified. public var downloader: ImageDownloader { if let item = lastMatchIgnoringAssociatedValue(.downloader(.default)), case .downloader(let downloader) = item { return downloader } return ImageDownloader.default } /// Member for animation transition when using UIImageView. public var transition: ImageTransition { if let item = lastMatchIgnoringAssociatedValue(.transition(.none)), case .transition(let transition) = item { return transition } return ImageTransition.none } /// A `Float` value set as the priority of image download task. The value for it should be /// between 0.0~1.0. public var downloadPriority: Float { if let item = lastMatchIgnoringAssociatedValue(.downloadPriority(0)), case .downloadPriority(let priority) = item { return priority } return URLSessionTask.defaultPriority } /// Whether an image will be always downloaded again or not. public var forceRefresh: Bool { return contains{ $0 <== .forceRefresh } } /// Whether an image should be got only from memory cache or download. public var fromMemoryCacheOrRefresh: Bool { return contains{ $0 <== .fromMemoryCacheOrRefresh } } /// Whether the transition should always happen or not. public var forceTransition: Bool { return contains{ $0 <== .forceTransition } } /// Whether cache the image only in memory or not. public var cacheMemoryOnly: Bool { return contains{ $0 <== .cacheMemoryOnly } } /// Whether only load the images from cache or not. public var onlyFromCache: Bool { return contains{ $0 <== .onlyFromCache } } /// Whether the image should be decoded in background or not. public var backgroundDecode: Bool { return contains{ $0 <== .backgroundDecode } } /// Whether the image data should be all loaded at once if it is an animated image. public var preloadAllAnimationData: Bool { return contains { $0 <== .preloadAllAnimationData } } /// The queue of callbacks should happen from Kingfisher. public var callbackDispatchQueue: DispatchQueue { if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)), case .callbackDispatchQueue(let queue) = item { return queue ?? DispatchQueue.main } return DispatchQueue.main } /// The scale factor which should be used for the image. public var scaleFactor: CGFloat { if let item = lastMatchIgnoringAssociatedValue(.scaleFactor(0)), case .scaleFactor(let scale) = item { return scale } return 1.0 } /// The `ImageDownloadRequestModifier` will be used before sending a download request. public var modifier: ImageDownloadRequestModifier { if let item = lastMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)), case .requestModifier(let modifier) = item { return modifier } return NoModifier.default } /// `ImageProcessor` for processing when the downloading finishes. public var processor: ImageProcessor { if let item = lastMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)), case .processor(let processor) = item { return processor } return DefaultImageProcessor.default } /// `CacheSerializer` to convert image to data for storing in cache. public var cacheSerializer: CacheSerializer { if let item = lastMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)), case .cacheSerializer(let cacheSerializer) = item { return cacheSerializer } return DefaultCacheSerializer.default } /// Keep the existing image while setting another image to an image view. /// Or the placeholder will be used while downloading. public var keepCurrentImageWhileLoading: Bool { return contains { $0 <== .keepCurrentImageWhileLoading } } public var onlyLoadFirstFrame: Bool { return contains { $0 <== .onlyLoadFirstFrame } } public var cacheOriginalImage: Bool { return contains { $0 <== .cacheOriginalImage } } } ================================================ FILE: Pods/Kingfisher/Sources/Placeholder.swift ================================================ // // Placeholder.swift // Kingfisher // // Created by Tieme van Veen on 28/08/2017. // // Copyright (c) 2017 Wei Wang // // 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. #if os(macOS) import AppKit #else import UIKit #endif /// Represent a placeholder type which could be set while loading as well as /// loading finished without getting an image. public protocol Placeholder { /// How the placeholder should be added to a given image view. func add(to imageView: ImageView) /// How the placeholder should be removed from a given image view. func remove(from imageView: ImageView) } /// Default implementation of an image placeholder. The image will be set or /// reset directly for `image` property of the image view. extension Placeholder where Self: Image { /// How the placeholder should be added to a given image view. public func add(to imageView: ImageView) { imageView.image = self } /// How the placeholder should be removed from a given image view. public func remove(from imageView: ImageView) { imageView.image = nil } } extension Image: Placeholder {} /// Default implementation of an arbitrary view as placeholder. The view will be /// added as a subview when adding and be removed from its super view when removing. /// /// To use your customize View type as placeholder, simply let it conforming to /// `Placeholder` by `extension MyView: Placeholder {}`. extension Placeholder where Self: View { /// How the placeholder should be added to a given image view. public func add(to imageView: ImageView) { imageView.addSubview(self) self.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0), NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0), NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0), NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0) ]) } /// How the placeholder should be removed from a given image view. public func remove(from imageView: ImageView) { self.removeFromSuperview() } } ================================================ FILE: Pods/Kingfisher/Sources/RequestModifier.swift ================================================ // // RequestModifier.swift // Kingfisher // // Created by Wei Wang on 2016/09/05. // // Copyright (c) 2017 Wei Wang // // 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. import Foundation /// Request modifier of image downloader. public protocol ImageDownloadRequestModifier { func modified(for request: URLRequest) -> URLRequest? } struct NoModifier: ImageDownloadRequestModifier { static let `default` = NoModifier() private init() {} func modified(for request: URLRequest) -> URLRequest? { return request } } public struct AnyModifier: ImageDownloadRequestModifier { let block: (URLRequest) -> URLRequest? public func modified(for request: URLRequest) -> URLRequest? { return block(request) } public init(modify: @escaping (URLRequest) -> URLRequest? ) { block = modify } } ================================================ FILE: Pods/Kingfisher/Sources/Resource.swift ================================================ // // Resource.swift // Kingfisher // // Created by Wei Wang on 15/4/6. // // Copyright (c) 2017 Wei Wang // // 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. import Foundation /// `Resource` protocol defines how to download and cache a resource from network. public protocol Resource { /// The key used in cache. var cacheKey: String { get } /// The target image URL. var downloadURL: URL { get } } /** ImageResource is a simple combination of `downloadURL` and `cacheKey`. When passed to image view set methods, Kingfisher will try to download the target image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. */ public struct ImageResource: Resource { /// The key used in cache. public let cacheKey: String /// The target image URL. public let downloadURL: URL /** Create a resource. - parameter downloadURL: The target image URL. - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. - returns: A resource. */ public init(downloadURL: URL, cacheKey: String? = nil) { self.downloadURL = downloadURL self.cacheKey = cacheKey ?? downloadURL.absoluteString } } /** URL conforms to `Resource` in Kingfisher. The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. If you need customize the url and/or cache key, use `ImageResource` instead. */ extension URL: Resource { public var cacheKey: String { return absoluteString } public var downloadURL: URL { return self } } ================================================ FILE: Pods/Kingfisher/Sources/String+MD5.swift ================================================ // // String+MD5.swift // Kingfisher // // To date, adding CommonCrypto to a Swift framework is problematic. See: // http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework // We're using a subset and modified version of CryptoSwift as an alternative. // The following is an altered source version that only includes MD5. The original software can be found at: // https://github.com/krzyzanowskim/CryptoSwift // This is the original copyright notice: /* Copyright (C) 2014 Marcin Krzyżanowski This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - This notice may not be removed or altered from any source or binary distribution. */ import Foundation public struct StringProxy { fileprivate let base: String init(proxy: String) { base = proxy } } extension String: KingfisherCompatible { public typealias CompatibleType = StringProxy public var kf: CompatibleType { return StringProxy(proxy: self) } } extension StringProxy { var md5: String { if let data = base.data(using: .utf8, allowLossyConversion: true) { let message = data.withUnsafeBytes { bytes -> [UInt8] in return Array(UnsafeBufferPointer(start: bytes, count: data.count)) } let MD5Calculator = MD5(message) let MD5Data = MD5Calculator.calculate() var MD5String = String() for c in MD5Data { MD5String += String(format: "%02x", c) } return MD5String } else { return base } } } /** array of bytes, little-endian representation */ func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] { let totalBytes = length ?? (MemoryLayout.size * 8) let valuePointer = UnsafeMutablePointer.allocate(capacity: 1) valuePointer.pointee = value let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in var bytes = [UInt8](repeating: 0, count: totalBytes) for j in 0...size, totalBytes) { bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee } return bytes } valuePointer.deinitialize() valuePointer.deallocate(capacity: 1) return bytes } extension Int { /** Array of bytes with optional padding (little-endian) */ func bytes(_ totalBytes: Int = MemoryLayout.size) -> [UInt8] { return arrayOfBytes(self, length: totalBytes) } } extension NSMutableData { /** Convenient way to append bytes */ func appendBytes(_ arrayOfBytes: [UInt8]) { append(arrayOfBytes, length: arrayOfBytes.count) } } protocol HashProtocol { var message: Array { get } /** Common part for hash calculation. Prepare header data. */ func prepare(_ len: Int) -> Array } extension HashProtocol { func prepare(_ len: Int) -> Array { var tmpMessage = message // Step 1. Append Padding Bits tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message // append "0" bit until message length in bits ≡ 448 (mod 512) var msgLength = tmpMessage.count var counter = 0 while msgLength % len != (len - 8) { counter += 1 msgLength += 1 } tmpMessage += Array(repeating: 0, count: counter) return tmpMessage } } func toUInt32Array(_ slice: ArraySlice) -> Array { var result = Array() result.reserveCapacity(16) for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout.size) { let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24 let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16 let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8 let d3 = UInt32(slice[idx]) let val: UInt32 = d0 | d1 | d2 | d3 result.append(val) } return result } struct BytesIterator: IteratorProtocol { let chunkSize: Int let data: [UInt8] init(chunkSize: Int, data: [UInt8]) { self.chunkSize = chunkSize self.data = data } var offset = 0 mutating func next() -> ArraySlice? { let end = min(chunkSize, data.count - offset) let result = data[offset.. 0 ? result : nil } } struct BytesSequence: Sequence { let chunkSize: Int let data: [UInt8] func makeIterator() -> BytesIterator { return BytesIterator(chunkSize: chunkSize, data: data) } } func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 { return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits)) } class MD5: HashProtocol { static let size = 16 // 128 / 8 let message: [UInt8] init (_ message: [UInt8]) { self.message = message } /** specifies the per-round shift amounts */ private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21] /** binary integer part of the sines of integers (Radians) */ private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391] private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] func calculate() -> [UInt8] { var tmpMessage = prepare(64) tmpMessage.reserveCapacity(tmpMessage.count + 4) // hash values var hh = hashes // Step 2. Append Length a 64-bit representation of lengthInBits let lengthInBits = (message.count * 8) let lengthBytes = lengthInBits.bytes(64 / 8) tmpMessage += lengthBytes.reversed() // Process the message in successive 512-bit chunks: let chunkSizeBytes = 512 / 8 // 64 for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) { // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 var M = toUInt32Array(chunk) assert(M.count == 16, "Invalid array") // Initialize hash value for this chunk: var A: UInt32 = hh[0] var B: UInt32 = hh[1] var C: UInt32 = hh[2] var D: UInt32 = hh[3] var dTemp: UInt32 = 0 // Main loop for j in 0 ..< sines.count { var g = 0 var F: UInt32 = 0 switch j { case 0...15: F = (B & C) | ((~B) & D) g = j break case 16...31: F = (D & B) | (~D & C) g = (5 * j + 1) % 16 break case 32...47: F = B ^ C ^ D g = (3 * j + 5) % 16 break case 48...63: F = C ^ (B | (~D)) g = (7 * j) % 16 break default: break } dTemp = D D = C C = B B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j]) A = dTemp } hh[0] = hh[0] &+ A hh[1] = hh[1] &+ B hh[2] = hh[2] &+ C hh[3] = hh[3] &+ D } var result = [UInt8]() result.reserveCapacity(hh.count / 4) hh.forEach { let itemLE = $0.littleEndian let r1 = UInt8(itemLE & 0xff) let r2 = UInt8((itemLE >> 8) & 0xff) let r3 = UInt8((itemLE >> 16) & 0xff) let r4 = UInt8((itemLE >> 24) & 0xff) result += [r1, r2, r3, r4] } return result } } ================================================ FILE: Pods/Kingfisher/Sources/ThreadHelper.swift ================================================ // // ThreadHelper.swift // Kingfisher // // Created by Wei Wang on 15/10/9. // // Copyright (c) 2017 Wei Wang // // 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. import Foundation extension DispatchQueue { // This method will dispatch the `block` to self. // If `self` is the main queue, and current thread is main thread, the block // will be invoked immediately instead of being dispatched. func safeAsync(_ block: @escaping ()->()) { if self === DispatchQueue.main && Thread.isMainThread { block() } else { async { block() } } } } ================================================ FILE: Pods/Kingfisher/Sources/UIButton+Kingfisher.swift ================================================ // // UIButton+Kingfisher.swift // Kingfisher // // Created by Wei Wang on 15/4/13. // // Copyright (c) 2017 Wei Wang // // 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. import UIKit // MARK: - Set Images /** * Set image to use in button from web for a specified state. */ extension Kingfisher where Base: UIButton { /** Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and completion handler. - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter state: The state that uses the specified image. - parameter placeholder: A placeholder image when retrieving the image at URL. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called when the image downloading progress gets updated. - parameter completionHandler: Called when the image retrieved and set. - returns: A task represents the retrieving process. - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. If `resource` is `nil`, the `placeholder` image will be set and `completionHandler` will be called with both `error` and `image` being `nil`. */ @discardableResult public func setImage(with resource: Resource?, for state: UIControlState, placeholder: UIImage? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { guard let resource = resource else { base.setImage(placeholder, for: state) setWebURL(nil, for: state) completionHandler?(nil, nil, .none, nil) return .empty } let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo) if !options.keepCurrentImageWhileLoading { base.setImage(placeholder, for: state) } setWebURL(resource.downloadURL, for: state) let task = KingfisherManager.shared.retrieveImage( with: resource, options: options, progressBlock: { receivedSize, totalSize in guard resource.downloadURL == self.webURL(for: state) else { return } if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, completionHandler: {[weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { guard let strongBase = base, imageURL == self.webURL(for: state) else { completionHandler?(image, error, cacheType, imageURL) return } self.setImageTask(nil) if image != nil { strongBase.setImage(image, for: state) } completionHandler?(image, error, cacheType, imageURL) } }) setImageTask(task) return task } /** Cancel the image download task bounded to the image view if it is running. Nothing will happen if the downloading has already finished. */ public func cancelImageDownloadTask() { imageTask?.cancel() } /** Set the background image to use for a specified state with a resource, a placeholder image, options progress handler and completion handler. - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter state: The state that uses the specified image. - parameter placeholder: A placeholder image when retrieving the image at URL. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called when the image downloading progress gets updated. - parameter completionHandler: Called when the image retrieved and set. - returns: A task represents the retrieving process. - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. If `resource` is `nil`, the `placeholder` image will be set and `completionHandler` will be called with both `error` and `image` being `nil`. */ @discardableResult public func setBackgroundImage(with resource: Resource?, for state: UIControlState, placeholder: UIImage? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { guard let resource = resource else { base.setBackgroundImage(placeholder, for: state) setBackgroundWebURL(nil, for: state) completionHandler?(nil, nil, .none, nil) return .empty } let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo) if !options.keepCurrentImageWhileLoading { base.setBackgroundImage(placeholder, for: state) } setBackgroundWebURL(resource.downloadURL, for: state) let task = KingfisherManager.shared.retrieveImage( with: resource, options: options, progressBlock: { receivedSize, totalSize in guard resource.downloadURL == self.backgroundWebURL(for: state) else { return } if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, completionHandler: { [weak base] image, error, cacheType, imageURL in DispatchQueue.main.safeAsync { guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else { completionHandler?(image, error, cacheType, imageURL) return } self.setBackgroundImageTask(nil) if image != nil { strongBase.setBackgroundImage(image, for: state) } completionHandler?(image, error, cacheType, imageURL) } }) setBackgroundImageTask(task) return task } /** Cancel the background image download task bounded to the image view if it is running. Nothing will happen if the downloading has already finished. */ public func cancelBackgroundImageDownloadTask() { backgroundImageTask?.cancel() } } // MARK: - Associated Object private var lastURLKey: Void? private var imageTaskKey: Void? extension Kingfisher where Base: UIButton { /** Get the image URL binded to this button for a specified state. - parameter state: The state that uses the specified image. - returns: Current URL for image. */ public func webURL(for state: UIControlState) -> URL? { return webURLs[NSNumber(value:state.rawValue)] as? URL } fileprivate func setWebURL(_ url: URL?, for state: UIControlState) { webURLs[NSNumber(value:state.rawValue)] = url } fileprivate var webURLs: NSMutableDictionary { var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary if dictionary == nil { dictionary = NSMutableDictionary() setWebURLs(dictionary!) } return dictionary! } fileprivate func setWebURLs(_ URLs: NSMutableDictionary) { objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } fileprivate var imageTask: RetrieveImageTask? { return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask } fileprivate func setImageTask(_ task: RetrieveImageTask?) { objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } private var lastBackgroundURLKey: Void? private var backgroundImageTaskKey: Void? extension Kingfisher where Base: UIButton { /** Get the background image URL binded to this button for a specified state. - parameter state: The state that uses the specified background image. - returns: Current URL for background image. */ public func backgroundWebURL(for state: UIControlState) -> URL? { return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL } fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControlState) { backgroundWebURLs[NSNumber(value:state.rawValue)] = url } fileprivate var backgroundWebURLs: NSMutableDictionary { var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary if dictionary == nil { dictionary = NSMutableDictionary() setBackgroundWebURLs(dictionary!) } return dictionary! } fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) { objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } fileprivate var backgroundImageTask: RetrieveImageTask? { return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask } fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) { objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } ================================================ FILE: Pods/MJRefresh/LICENSE ================================================ Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) 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. ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h ================================================ // // MJRefreshAutoFooter.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshFooter.h" @interface MJRefreshAutoFooter : MJRefreshFooter /** 是否自动刷新(默认为YES) */ @property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh; /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ @property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("请使用triggerAutomaticallyRefreshPercent属性"); /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ @property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m ================================================ // // MJRefreshAutoFooter.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshAutoFooter.h" @interface MJRefreshAutoFooter() @end @implementation MJRefreshAutoFooter #pragma mark - 初始化 - (void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; if (newSuperview) { // 新的父控件 if (self.hidden == NO) { self.scrollView.mj_insetB += self.mj_h; } // 设置位置 self.mj_y = _scrollView.mj_contentH; } else { // 被移除了 if (self.hidden == NO) { self.scrollView.mj_insetB -= self.mj_h; } } } #pragma mark - 过期方法 - (void)setAppearencePercentTriggerAutoRefresh:(CGFloat)appearencePercentTriggerAutoRefresh { self.triggerAutomaticallyRefreshPercent = appearencePercentTriggerAutoRefresh; } - (CGFloat)appearencePercentTriggerAutoRefresh { return self.triggerAutomaticallyRefreshPercent; } #pragma mark - 实现父类的方法 - (void)prepare { [super prepare]; // 默认底部控件100%出现时才会自动刷新 self.triggerAutomaticallyRefreshPercent = 1.0; // 设置为默认状态 self.automaticallyRefresh = YES; } - (void)scrollViewContentSizeDidChange:(NSDictionary *)change { [super scrollViewContentSizeDidChange:change]; // 设置位置 self.mj_y = self.scrollView.mj_contentH; } - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change { [super scrollViewContentOffsetDidChange:change]; if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return; if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) { // 内容超过一个屏幕 // 这里的_scrollView.mj_contentH替换掉self.mj_y更为合理 if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) { // 防止手松开时连续调用 CGPoint old = [change[@"old"] CGPointValue]; CGPoint new = [change[@"new"] CGPointValue]; if (new.y <= old.y) return; // 当底部刷新控件完全出现时,才刷新 [self beginRefreshing]; } } } - (void)scrollViewPanStateDidChange:(NSDictionary *)change { [super scrollViewPanStateDidChange:change]; if (self.state != MJRefreshStateIdle) return; if (_scrollView.panGestureRecognizer.state == UIGestureRecognizerStateEnded) {// 手松开 if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) { // 不够一个屏幕 if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽 [self beginRefreshing]; } } else { // 超出一个屏幕 if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) { [self beginRefreshing]; } } } } - (void)setState:(MJRefreshState)state { MJRefreshCheckState if (state == MJRefreshStateRefreshing) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self executeRefreshingCallback]; }); } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { if (MJRefreshStateRefreshing == oldState) { if (self.endRefreshingCompletionBlock) { self.endRefreshingCompletionBlock(); } } } } - (void)setHidden:(BOOL)hidden { BOOL lastHidden = self.isHidden; [super setHidden:hidden]; if (!lastHidden && hidden) { self.state = MJRefreshStateIdle; self.scrollView.mj_insetB -= self.mj_h; } else if (lastHidden && !hidden) { self.scrollView.mj_insetB += self.mj_h; // 设置位置 self.mj_y = _scrollView.mj_contentH; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h ================================================ // // MJRefreshBackFooter.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshFooter.h" @interface MJRefreshBackFooter : MJRefreshFooter @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m ================================================ // // MJRefreshBackFooter.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshBackFooter.h" @interface MJRefreshBackFooter() @property (assign, nonatomic) NSInteger lastRefreshCount; @property (assign, nonatomic) CGFloat lastBottomDelta; @end @implementation MJRefreshBackFooter #pragma mark - 初始化 - (void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; [self scrollViewContentSizeDidChange:nil]; } #pragma mark - 实现父类的方法 - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change { [super scrollViewContentOffsetDidChange:change]; // 如果正在刷新,直接返回 if (self.state == MJRefreshStateRefreshing) return; _scrollViewOriginalInset = self.scrollView.mj_inset; // 当前的contentOffset CGFloat currentOffsetY = self.scrollView.mj_offsetY; // 尾部控件刚好出现的offsetY CGFloat happenOffsetY = [self happenOffsetY]; // 如果是向下滚动到看不见尾部控件,直接返回 if (currentOffsetY <= happenOffsetY) return; CGFloat pullingPercent = (currentOffsetY - happenOffsetY) / self.mj_h; // 如果已全部加载,仅设置pullingPercent,然后返回 if (self.state == MJRefreshStateNoMoreData) { self.pullingPercent = pullingPercent; return; } if (self.scrollView.isDragging) { self.pullingPercent = pullingPercent; // 普通 和 即将刷新 的临界点 CGFloat normal2pullingOffsetY = happenOffsetY + self.mj_h; if (self.state == MJRefreshStateIdle && currentOffsetY > normal2pullingOffsetY) { // 转为即将刷新状态 self.state = MJRefreshStatePulling; } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) { // 转为普通状态 self.state = MJRefreshStateIdle; } } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 // 开始刷新 [self beginRefreshing]; } else if (pullingPercent < 1) { self.pullingPercent = pullingPercent; } } - (void)scrollViewContentSizeDidChange:(NSDictionary *)change { [super scrollViewContentSizeDidChange:change]; // 内容的高度 CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom; // 表格的高度 CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom; // 设置位置和尺寸 self.mj_y = MAX(contentHeight, scrollHeight); } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态来设置属性 if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { // 刷新完毕 if (MJRefreshStateRefreshing == oldState) { [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ self.scrollView.mj_insetB -= self.lastBottomDelta; // 自动调整透明度 if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; } completion:^(BOOL finished) { self.pullingPercent = 0.0; if (self.endRefreshingCompletionBlock) { self.endRefreshingCompletionBlock(); } }]; } CGFloat deltaH = [self heightForContentBreakView]; // 刚刷新完毕 if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) { self.scrollView.mj_offsetY = self.scrollView.mj_offsetY; } } else if (state == MJRefreshStateRefreshing) { // 记录刷新前的数量 self.lastRefreshCount = self.scrollView.mj_totalDataCount; [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom; CGFloat deltaH = [self heightForContentBreakView]; if (deltaH < 0) { // 如果内容高度小于view的高度 bottom -= deltaH; } self.lastBottomDelta = bottom - self.scrollView.mj_insetB; self.scrollView.mj_insetB = bottom; self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h; } completion:^(BOOL finished) { [self executeRefreshingCallback]; }]; } } #pragma mark - 私有方法 #pragma mark 获得scrollView的内容 超出 view 的高度 - (CGFloat)heightForContentBreakView { CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top; return self.scrollView.contentSize.height - h; } #pragma mark 刚好看到上拉刷新控件时的contentOffset.y - (CGFloat)happenOffsetY { CGFloat deltaH = [self heightForContentBreakView]; if (deltaH > 0) { return deltaH - self.scrollViewOriginalInset.top; } else { return - self.scrollViewOriginalInset.top; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // MJRefreshComponent.h // MJRefreshExample // // Created by MJ Lee on 15/3/4. // Copyright (c) 2015年 小码哥. All rights reserved. // 刷新控件的基类 #import #import "MJRefreshConst.h" #import "UIView+MJExtension.h" #import "UIScrollView+MJExtension.h" #import "UIScrollView+MJRefresh.h" #import "NSBundle+MJRefresh.h" /** 刷新控件的状态 */ typedef NS_ENUM(NSInteger, MJRefreshState) { /** 普通闲置状态 */ MJRefreshStateIdle = 1, /** 松开就可以进行刷新的状态 */ MJRefreshStatePulling, /** 正在刷新中的状态 */ MJRefreshStateRefreshing, /** 即将刷新的状态 */ MJRefreshStateWillRefresh, /** 所有数据加载完毕,没有更多的数据了 */ MJRefreshStateNoMoreData }; /** 进入刷新状态的回调 */ typedef void (^MJRefreshComponentRefreshingBlock)(void); /** 开始刷新后的回调(进入刷新状态后的回调) */ typedef void (^MJRefreshComponentbeginRefreshingCompletionBlock)(void); /** 结束刷新后的回调 */ typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void); /** 刷新控件的基类 */ @interface MJRefreshComponent : UIView { /** 记录scrollView刚开始的inset */ UIEdgeInsets _scrollViewOriginalInset; /** 父控件 */ __weak UIScrollView *_scrollView; } #pragma mark - 刷新回调 /** 正在刷新的回调 */ @property (copy, nonatomic) MJRefreshComponentRefreshingBlock refreshingBlock; /** 设置回调对象和回调方法 */ - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action; /** 回调对象 */ @property (weak, nonatomic) id refreshingTarget; /** 回调方法 */ @property (assign, nonatomic) SEL refreshingAction; /** 触发回调(交给子类去调用) */ - (void)executeRefreshingCallback; #pragma mark - 刷新状态控制 /** 进入刷新状态 */ - (void)beginRefreshing; - (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock; /** 开始刷新后的回调(进入刷新状态后的回调) */ @property (copy, nonatomic) MJRefreshComponentbeginRefreshingCompletionBlock beginRefreshingCompletionBlock; /** 结束刷新的回调 */ @property (copy, nonatomic) MJRefreshComponentEndRefreshingCompletionBlock endRefreshingCompletionBlock; /** 结束刷新状态 */ - (void)endRefreshing; - (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock; /** 是否正在刷新 */ @property (assign, nonatomic, readonly, getter=isRefreshing) BOOL refreshing; //- (BOOL)isRefreshing; /** 刷新状态 一般交给子类内部实现 */ @property (assign, nonatomic) MJRefreshState state; #pragma mark - 交给子类去访问 /** 记录scrollView刚开始的inset */ @property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset; /** 父控件 */ @property (weak, nonatomic, readonly) UIScrollView *scrollView; #pragma mark - 交给子类们去实现 /** 初始化 */ - (void)prepare NS_REQUIRES_SUPER; /** 摆放子控件frame */ - (void)placeSubviews NS_REQUIRES_SUPER; /** 当scrollView的contentOffset发生改变的时候调用 */ - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; /** 当scrollView的contentSize发生改变的时候调用 */ - (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; /** 当scrollView的拖拽状态发生改变的时候调用 */ - (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; #pragma mark - 其他 /** 拉拽的百分比(交给子类重写) */ @property (assign, nonatomic) CGFloat pullingPercent; /** 根据拖拽比例自动切换透明度 */ @property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性"); /** 根据拖拽比例自动切换透明度 */ @property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha; @end @interface UILabel(MJRefresh) + (instancetype)mj_label; - (CGFloat)mj_textWith; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // MJRefreshComponent.m // MJRefreshExample // // Created by MJ Lee on 15/3/4. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshComponent.h" #import "MJRefreshConst.h" @interface MJRefreshComponent() @property (strong, nonatomic) UIPanGestureRecognizer *pan; @end @implementation MJRefreshComponent #pragma mark - 初始化 - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // 准备工作 [self prepare]; // 默认是普通状态 self.state = MJRefreshStateIdle; } return self; } - (void)prepare { // 基本属性 self.autoresizingMask = UIViewAutoresizingFlexibleWidth; self.backgroundColor = [UIColor clearColor]; } - (void)layoutSubviews { [self placeSubviews]; [super layoutSubviews]; } - (void)placeSubviews{} - (void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; // 如果不是UIScrollView,不做任何事情 if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return; // 旧的父控件移除监听 [self removeObservers]; if (newSuperview) { // 新的父控件 // 设置宽度 self.mj_w = newSuperview.mj_w; // 设置位置 self.mj_x = -_scrollView.mj_insetL; // 记录UIScrollView _scrollView = (UIScrollView *)newSuperview; // 设置永远支持垂直弹簧效果 _scrollView.alwaysBounceVertical = YES; // 记录UIScrollView最开始的contentInset _scrollViewOriginalInset = _scrollView.mj_inset; // 添加监听 [self addObservers]; } } - (void)drawRect:(CGRect)rect { [super drawRect:rect]; if (self.state == MJRefreshStateWillRefresh) { // 预防view还没显示出来就调用了beginRefreshing self.state = MJRefreshStateRefreshing; } } #pragma mark - KVO监听 - (void)addObservers { NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld; [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil]; [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil]; self.pan = self.scrollView.panGestureRecognizer; [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil]; } - (void)removeObservers { [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset]; [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize]; [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState]; self.pan = nil; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // 遇到这些情况就直接返回 if (!self.userInteractionEnabled) return; // 这个就算看不见也需要处理 if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) { [self scrollViewContentSizeDidChange:change]; } // 看不见 if (self.hidden) return; if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) { [self scrollViewContentOffsetDidChange:change]; } else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) { [self scrollViewPanStateDidChange:change]; } } - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{} - (void)scrollViewContentSizeDidChange:(NSDictionary *)change{} - (void)scrollViewPanStateDidChange:(NSDictionary *)change{} #pragma mark - 公共方法 #pragma mark 设置回调对象和回调方法 - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action { self.refreshingTarget = target; self.refreshingAction = action; } - (void)setState:(MJRefreshState)state { _state = state; // 加入主队列的目的是等setState:方法调用完毕、设置完文字后再去布局子控件 dispatch_async(dispatch_get_main_queue(), ^{ [self setNeedsLayout]; }); } #pragma mark 进入刷新状态 - (void)beginRefreshing { [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ self.alpha = 1.0; }]; self.pullingPercent = 1.0; // 只要正在刷新,就完全显示 if (self.window) { self.state = MJRefreshStateRefreshing; } else { // 预防正在刷新中时,调用本方法使得header inset回置失败 if (self.state != MJRefreshStateRefreshing) { self.state = MJRefreshStateWillRefresh; // 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下) [self setNeedsDisplay]; } } } - (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock { self.beginRefreshingCompletionBlock = completionBlock; [self beginRefreshing]; } #pragma mark 结束刷新状态 - (void)endRefreshing { dispatch_async(dispatch_get_main_queue(), ^{ self.state = MJRefreshStateIdle; }); } - (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock { self.endRefreshingCompletionBlock = completionBlock; [self endRefreshing]; } #pragma mark 是否正在刷新 - (BOOL)isRefreshing { return self.state == MJRefreshStateRefreshing || self.state == MJRefreshStateWillRefresh; } #pragma mark 自动切换透明度 - (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha { self.automaticallyChangeAlpha = autoChangeAlpha; } - (BOOL)isAutoChangeAlpha { return self.isAutomaticallyChangeAlpha; } - (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha { _automaticallyChangeAlpha = automaticallyChangeAlpha; if (self.isRefreshing) return; if (automaticallyChangeAlpha) { self.alpha = self.pullingPercent; } else { self.alpha = 1.0; } } #pragma mark 根据拖拽进度设置透明度 - (void)setPullingPercent:(CGFloat)pullingPercent { _pullingPercent = pullingPercent; if (self.isRefreshing) return; if (self.isAutomaticallyChangeAlpha) { self.alpha = pullingPercent; } } #pragma mark - 内部方法 - (void)executeRefreshingCallback { dispatch_async(dispatch_get_main_queue(), ^{ if (self.refreshingBlock) { self.refreshingBlock(); } if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) { MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self); } if (self.beginRefreshingCompletionBlock) { self.beginRefreshingCompletionBlock(); } }); } @end @implementation UILabel(MJRefresh) + (instancetype)mj_label { UILabel *label = [[self alloc] init]; label.font = MJRefreshLabelFont; label.textColor = MJRefreshLabelTextColor; label.autoresizingMask = UIViewAutoresizingFlexibleWidth; label.textAlignment = NSTextAlignmentCenter; label.backgroundColor = [UIColor clearColor]; return label; } - (CGFloat)mj_textWith { CGFloat stringWidth = 0; CGSize size = CGSizeMake(MAXFLOAT, MAXFLOAT); if (self.text.length > 0) { #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 stringWidth =[self.text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.font} context:nil].size.width; #else stringWidth = [self.text sizeWithFont:self.font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping].width; #endif } return stringWidth; } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // MJRefreshFooter.h // MJRefreshExample // // Created by MJ Lee on 15/3/5. // Copyright (c) 2015年 小码哥. All rights reserved. // 上拉刷新控件 #import "MJRefreshComponent.h" @interface MJRefreshFooter : MJRefreshComponent /** 创建footer */ + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; /** 创建footer */ + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; /** 提示没有更多的数据 */ - (void)endRefreshingWithNoMoreData; - (void)noticeNoMoreData MJRefreshDeprecated("使用endRefreshingWithNoMoreData"); /** 重置没有更多的数据(消除没有更多数据的状态) */ - (void)resetNoMoreData; /** 忽略多少scrollView的contentInset的bottom */ @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom; /** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */ @property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden MJRefreshDeprecated("不建议使用此属性,开发者请自行控制footer的显示和隐藏。基于安全考虑,在未来的某些版本此属性可能作废"); @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // MJRefreshFooter.m // MJRefreshExample // // Created by MJ Lee on 15/3/5. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshFooter.h" #include "UIScrollView+MJRefresh.h" @interface MJRefreshFooter() @end @implementation MJRefreshFooter #pragma mark - 构造方法 + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock { MJRefreshFooter *cmp = [[self alloc] init]; cmp.refreshingBlock = refreshingBlock; return cmp; } + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action { MJRefreshFooter *cmp = [[self alloc] init]; [cmp setRefreshingTarget:target refreshingAction:action]; return cmp; } #pragma mark - 重写父类的方法 - (void)prepare { [super prepare]; // 设置自己的高度 self.mj_h = MJRefreshFooterHeight; // 默认不会自动隐藏 self.automaticallyHidden = NO; } - (void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; if (newSuperview) { // 监听scrollView数据的变化 if ([self.scrollView isKindOfClass:[UITableView class]] || [self.scrollView isKindOfClass:[UICollectionView class]]) { [self.scrollView setMj_reloadDataBlock:^(NSInteger totalDataCount) { if (self.isAutomaticallyHidden) { self.hidden = (totalDataCount == 0); } }]; } } } #pragma mark - 公共方法 - (void)endRefreshingWithNoMoreData { dispatch_async(dispatch_get_main_queue(), ^{ self.state = MJRefreshStateNoMoreData; }); } - (void)noticeNoMoreData { [self endRefreshingWithNoMoreData]; } - (void)resetNoMoreData { dispatch_async(dispatch_get_main_queue(), ^{ self.state = MJRefreshStateIdle; }); } - (void)setAutomaticallyHidden:(BOOL)automaticallyHidden { _automaticallyHidden = automaticallyHidden; } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // MJRefreshHeader.h // MJRefreshExample // // Created by MJ Lee on 15/3/4. // Copyright (c) 2015年 小码哥. All rights reserved. // 下拉刷新控件:负责监控用户下拉的状态 #import "MJRefreshComponent.h" @interface MJRefreshHeader : MJRefreshComponent /** 创建header */ + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; /** 创建header */ + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; /** 这个key用来存储上一次下拉刷新成功的时间 */ @property (copy, nonatomic) NSString *lastUpdatedTimeKey; /** 上一次下拉刷新成功的时间 */ @property (strong, nonatomic, readonly) NSDate *lastUpdatedTime; /** 忽略多少scrollView的contentInset的top */ @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // MJRefreshHeader.m // MJRefreshExample // // Created by MJ Lee on 15/3/4. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshHeader.h" @interface MJRefreshHeader() @property (assign, nonatomic) CGFloat insetTDelta; @end @implementation MJRefreshHeader #pragma mark - 构造方法 + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock { MJRefreshHeader *cmp = [[self alloc] init]; cmp.refreshingBlock = refreshingBlock; return cmp; } + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action { MJRefreshHeader *cmp = [[self alloc] init]; [cmp setRefreshingTarget:target refreshingAction:action]; return cmp; } #pragma mark - 覆盖父类的方法 - (void)prepare { [super prepare]; // 设置key self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey; // 设置高度 self.mj_h = MJRefreshHeaderHeight; } - (void)placeSubviews { [super placeSubviews]; // 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值) self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop; } - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change { [super scrollViewContentOffsetDidChange:change]; // 在刷新的refreshing状态 if (self.state == MJRefreshStateRefreshing) { // 暂时保留 if (self.window == nil) return; // sectionheader停留解决 CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top; insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT; self.scrollView.mj_insetT = insetT; self.insetTDelta = _scrollViewOriginalInset.top - insetT; return; } // 跳转到下一个控制器时,contentInset可能会变 _scrollViewOriginalInset = self.scrollView.mj_inset; // 当前的contentOffset CGFloat offsetY = self.scrollView.mj_offsetY; // 头部控件刚好出现的offsetY CGFloat happenOffsetY = - self.scrollViewOriginalInset.top; // 如果是向上滚动到看不见头部控件,直接返回 // >= -> > if (offsetY > happenOffsetY) return; // 普通 和 即将刷新 的临界点 CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h; CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h; if (self.scrollView.isDragging) { // 如果正在拖拽 self.pullingPercent = pullingPercent; if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) { // 转为即将刷新状态 self.state = MJRefreshStatePulling; } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) { // 转为普通状态 self.state = MJRefreshStateIdle; } } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 // 开始刷新 [self beginRefreshing]; } else if (pullingPercent < 1) { self.pullingPercent = pullingPercent; } } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态做事情 if (state == MJRefreshStateIdle) { if (oldState != MJRefreshStateRefreshing) return; // 保存刷新时间 [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey]; [[NSUserDefaults standardUserDefaults] synchronize]; // 恢复inset和offset [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ self.scrollView.mj_insetT += self.insetTDelta; // 自动调整透明度 if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; } completion:^(BOOL finished) { self.pullingPercent = 0.0; if (self.endRefreshingCompletionBlock) { self.endRefreshingCompletionBlock(); } }]; } else if (state == MJRefreshStateRefreshing) { dispatch_async(dispatch_get_main_queue(), ^{ [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ CGFloat top = self.scrollViewOriginalInset.top + self.mj_h; // 增加滚动区域top self.scrollView.mj_insetT = top; // 设置滚动位置 CGPoint offset = self.scrollView.contentOffset; offset.y = -top; [self.scrollView setContentOffset:offset animated:NO]; } completion:^(BOOL finished) { [self executeRefreshingCallback]; }]; }); } } #pragma mark - 公共方法 - (NSDate *)lastUpdatedTime { return [[NSUserDefaults standardUserDefaults] objectForKey:self.lastUpdatedTimeKey]; } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h ================================================ // // MJRefreshAutoGifFooter.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshAutoStateFooter.h" @interface MJRefreshAutoGifFooter : MJRefreshAutoStateFooter @property (weak, nonatomic, readonly) UIImageView *gifView; /** 设置state状态下的动画图片images 动画持续时间duration*/ - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; - (void)setImages:(NSArray *)images forState:(MJRefreshState)state; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m ================================================ // // MJRefreshAutoGifFooter.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshAutoGifFooter.h" @interface MJRefreshAutoGifFooter() { __unsafe_unretained UIImageView *_gifView; } /** 所有状态对应的动画图片 */ @property (strong, nonatomic) NSMutableDictionary *stateImages; /** 所有状态对应的动画时间 */ @property (strong, nonatomic) NSMutableDictionary *stateDurations; @end @implementation MJRefreshAutoGifFooter #pragma mark - 懒加载 - (UIImageView *)gifView { if (!_gifView) { UIImageView *gifView = [[UIImageView alloc] init]; [self addSubview:_gifView = gifView]; } return _gifView; } - (NSMutableDictionary *)stateImages { if (!_stateImages) { self.stateImages = [NSMutableDictionary dictionary]; } return _stateImages; } - (NSMutableDictionary *)stateDurations { if (!_stateDurations) { self.stateDurations = [NSMutableDictionary dictionary]; } return _stateDurations; } #pragma mark - 公共方法 - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state { if (images == nil) return; self.stateImages[@(state)] = images; self.stateDurations[@(state)] = @(duration); /* 根据图片设置控件的高度 */ UIImage *image = [images firstObject]; if (image.size.height > self.mj_h) { self.mj_h = image.size.height; } } - (void)setImages:(NSArray *)images forState:(MJRefreshState)state { [self setImages:images duration:images.count * 0.1 forState:state]; } #pragma mark - 实现父类的方法 - (void)prepare { [super prepare]; // 初始化间距 self.labelLeftInset = 20; } - (void)placeSubviews { [super placeSubviews]; if (self.gifView.constraints.count) return; self.gifView.frame = self.bounds; if (self.isRefreshingTitleHidden) { self.gifView.contentMode = UIViewContentModeCenter; } else { self.gifView.contentMode = UIViewContentModeRight; self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWith * 0.5; } } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态做事情 if (state == MJRefreshStateRefreshing) { NSArray *images = self.stateImages[@(state)]; if (images.count == 0) return; [self.gifView stopAnimating]; self.gifView.hidden = NO; if (images.count == 1) { // 单张图片 self.gifView.image = [images lastObject]; } else { // 多张图片 self.gifView.animationImages = images; self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; [self.gifView startAnimating]; } } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { [self.gifView stopAnimating]; self.gifView.hidden = YES; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h ================================================ // // MJRefreshAutoNormalFooter.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshAutoStateFooter.h" @interface MJRefreshAutoNormalFooter : MJRefreshAutoStateFooter /** 菊花的样式 */ @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m ================================================ // // MJRefreshAutoNormalFooter.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshAutoNormalFooter.h" @interface MJRefreshAutoNormalFooter() @property (weak, nonatomic) UIActivityIndicatorView *loadingView; @end @implementation MJRefreshAutoNormalFooter #pragma mark - 懒加载子控件 - (UIActivityIndicatorView *)loadingView { if (!_loadingView) { UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; loadingView.hidesWhenStopped = YES; [self addSubview:_loadingView = loadingView]; } return _loadingView; } - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle { _activityIndicatorViewStyle = activityIndicatorViewStyle; self.loadingView = nil; [self setNeedsLayout]; } #pragma mark - 重写父类的方法 - (void)prepare { [super prepare]; self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; } - (void)placeSubviews { [super placeSubviews]; if (self.loadingView.constraints.count) return; // 圈圈 CGFloat loadingCenterX = self.mj_w * 0.5; if (!self.isRefreshingTitleHidden) { loadingCenterX -= self.stateLabel.mj_textWith * 0.5 + self.labelLeftInset; } CGFloat loadingCenterY = self.mj_h * 0.5; self.loadingView.center = CGPointMake(loadingCenterX, loadingCenterY); } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态做事情 if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { [self.loadingView stopAnimating]; } else if (state == MJRefreshStateRefreshing) { [self.loadingView startAnimating]; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h ================================================ // // MJRefreshAutoStateFooter.h // MJRefreshExample // // Created by MJ Lee on 15/6/13. // Copyright © 2015年 小码哥. All rights reserved. // #import "MJRefreshAutoFooter.h" @interface MJRefreshAutoStateFooter : MJRefreshAutoFooter /** 文字距离圈圈、箭头的距离 */ @property (assign, nonatomic) CGFloat labelLeftInset; /** 显示刷新状态的label */ @property (weak, nonatomic, readonly) UILabel *stateLabel; /** 设置state状态下的文字 */ - (void)setTitle:(NSString *)title forState:(MJRefreshState)state; /** 隐藏刷新状态的文字 */ @property (assign, nonatomic, getter=isRefreshingTitleHidden) BOOL refreshingTitleHidden; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m ================================================ // // MJRefreshAutoStateFooter.m // MJRefreshExample // // Created by MJ Lee on 15/6/13. // Copyright © 2015年 小码哥. All rights reserved. // #import "MJRefreshAutoStateFooter.h" @interface MJRefreshAutoStateFooter() { /** 显示刷新状态的label */ __unsafe_unretained UILabel *_stateLabel; } /** 所有状态对应的文字 */ @property (strong, nonatomic) NSMutableDictionary *stateTitles; @end @implementation MJRefreshAutoStateFooter #pragma mark - 懒加载 - (NSMutableDictionary *)stateTitles { if (!_stateTitles) { self.stateTitles = [NSMutableDictionary dictionary]; } return _stateTitles; } - (UILabel *)stateLabel { if (!_stateLabel) { [self addSubview:_stateLabel = [UILabel mj_label]]; } return _stateLabel; } #pragma mark - 公共方法 - (void)setTitle:(NSString *)title forState:(MJRefreshState)state { if (title == nil) return; self.stateTitles[@(state)] = title; self.stateLabel.text = self.stateTitles[@(self.state)]; } #pragma mark - 私有方法 - (void)stateLabelClick { if (self.state == MJRefreshStateIdle) { [self beginRefreshing]; } } #pragma mark - 重写父类的方法 - (void)prepare { [super prepare]; // 初始化间距 self.labelLeftInset = MJRefreshLabelLeftInset; // 初始化文字 [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterIdleText] forState:MJRefreshStateIdle]; [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterRefreshingText] forState:MJRefreshStateRefreshing]; [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterNoMoreDataText] forState:MJRefreshStateNoMoreData]; // 监听label self.stateLabel.userInteractionEnabled = YES; [self.stateLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stateLabelClick)]]; } - (void)placeSubviews { [super placeSubviews]; if (self.stateLabel.constraints.count) return; // 状态标签 self.stateLabel.frame = self.bounds; } - (void)setState:(MJRefreshState)state { MJRefreshCheckState if (self.isRefreshingTitleHidden && state == MJRefreshStateRefreshing) { self.stateLabel.text = nil; } else { self.stateLabel.text = self.stateTitles[@(state)]; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h ================================================ // // MJRefreshBackGifFooter.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshBackStateFooter.h" @interface MJRefreshBackGifFooter : MJRefreshBackStateFooter @property (weak, nonatomic, readonly) UIImageView *gifView; /** 设置state状态下的动画图片images 动画持续时间duration*/ - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; - (void)setImages:(NSArray *)images forState:(MJRefreshState)state; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m ================================================ // // MJRefreshBackGifFooter.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshBackGifFooter.h" @interface MJRefreshBackGifFooter() { __unsafe_unretained UIImageView *_gifView; } /** 所有状态对应的动画图片 */ @property (strong, nonatomic) NSMutableDictionary *stateImages; /** 所有状态对应的动画时间 */ @property (strong, nonatomic) NSMutableDictionary *stateDurations; @end @implementation MJRefreshBackGifFooter #pragma mark - 懒加载 - (UIImageView *)gifView { if (!_gifView) { UIImageView *gifView = [[UIImageView alloc] init]; [self addSubview:_gifView = gifView]; } return _gifView; } - (NSMutableDictionary *)stateImages { if (!_stateImages) { self.stateImages = [NSMutableDictionary dictionary]; } return _stateImages; } - (NSMutableDictionary *)stateDurations { if (!_stateDurations) { self.stateDurations = [NSMutableDictionary dictionary]; } return _stateDurations; } #pragma mark - 公共方法 - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state { if (images == nil) return; self.stateImages[@(state)] = images; self.stateDurations[@(state)] = @(duration); /* 根据图片设置控件的高度 */ UIImage *image = [images firstObject]; if (image.size.height > self.mj_h) { self.mj_h = image.size.height; } } - (void)setImages:(NSArray *)images forState:(MJRefreshState)state { [self setImages:images duration:images.count * 0.1 forState:state]; } #pragma mark - 实现父类的方法 - (void)prepare { [super prepare]; // 初始化间距 self.labelLeftInset = 20; } - (void)setPullingPercent:(CGFloat)pullingPercent { [super setPullingPercent:pullingPercent]; NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; if (self.state != MJRefreshStateIdle || images.count == 0) return; [self.gifView stopAnimating]; NSUInteger index = images.count * pullingPercent; if (index >= images.count) index = images.count - 1; self.gifView.image = images[index]; } - (void)placeSubviews { [super placeSubviews]; if (self.gifView.constraints.count) return; self.gifView.frame = self.bounds; if (self.stateLabel.hidden) { self.gifView.contentMode = UIViewContentModeCenter; } else { self.gifView.contentMode = UIViewContentModeRight; self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWith * 0.5; } } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态做事情 if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { NSArray *images = self.stateImages[@(state)]; if (images.count == 0) return; self.gifView.hidden = NO; [self.gifView stopAnimating]; if (images.count == 1) { // 单张图片 self.gifView.image = [images lastObject]; } else { // 多张图片 self.gifView.animationImages = images; self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; [self.gifView startAnimating]; } } else if (state == MJRefreshStateIdle) { self.gifView.hidden = NO; } else if (state == MJRefreshStateNoMoreData) { self.gifView.hidden = YES; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h ================================================ // // MJRefreshBackNormalFooter.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshBackStateFooter.h" @interface MJRefreshBackNormalFooter : MJRefreshBackStateFooter @property (weak, nonatomic, readonly) UIImageView *arrowView; /** 菊花的样式 */ @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m ================================================ // // MJRefreshBackNormalFooter.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshBackNormalFooter.h" #import "NSBundle+MJRefresh.h" @interface MJRefreshBackNormalFooter() { __unsafe_unretained UIImageView *_arrowView; } @property (weak, nonatomic) UIActivityIndicatorView *loadingView; @end @implementation MJRefreshBackNormalFooter #pragma mark - 懒加载子控件 - (UIImageView *)arrowView { if (!_arrowView) { UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]]; [self addSubview:_arrowView = arrowView]; } return _arrowView; } - (UIActivityIndicatorView *)loadingView { if (!_loadingView) { UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; loadingView.hidesWhenStopped = YES; [self addSubview:_loadingView = loadingView]; } return _loadingView; } - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle { _activityIndicatorViewStyle = activityIndicatorViewStyle; self.loadingView = nil; [self setNeedsLayout]; } #pragma mark - 重写父类的方法 - (void)prepare { [super prepare]; self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; } - (void)placeSubviews { [super placeSubviews]; // 箭头的中心点 CGFloat arrowCenterX = self.mj_w * 0.5; if (!self.stateLabel.hidden) { arrowCenterX -= self.labelLeftInset + self.stateLabel.mj_textWith * 0.5; } CGFloat arrowCenterY = self.mj_h * 0.5; CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); // 箭头 if (self.arrowView.constraints.count == 0) { self.arrowView.mj_size = self.arrowView.image.size; self.arrowView.center = arrowCenter; } // 圈圈 if (self.loadingView.constraints.count == 0) { self.loadingView.center = arrowCenter; } self.arrowView.tintColor = self.stateLabel.textColor; } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态做事情 if (state == MJRefreshStateIdle) { if (oldState == MJRefreshStateRefreshing) { self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ self.loadingView.alpha = 0.0; } completion:^(BOOL finished) { self.loadingView.alpha = 1.0; [self.loadingView stopAnimating]; self.arrowView.hidden = NO; }]; } else { self.arrowView.hidden = NO; [self.loadingView stopAnimating]; [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); }]; } } else if (state == MJRefreshStatePulling) { self.arrowView.hidden = NO; [self.loadingView stopAnimating]; [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ self.arrowView.transform = CGAffineTransformIdentity; }]; } else if (state == MJRefreshStateRefreshing) { self.arrowView.hidden = YES; [self.loadingView startAnimating]; } else if (state == MJRefreshStateNoMoreData) { self.arrowView.hidden = YES; [self.loadingView stopAnimating]; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h ================================================ // // MJRefreshBackStateFooter.h // MJRefreshExample // // Created by MJ Lee on 15/6/13. // Copyright © 2015年 小码哥. All rights reserved. // #import "MJRefreshBackFooter.h" @interface MJRefreshBackStateFooter : MJRefreshBackFooter /** 文字距离圈圈、箭头的距离 */ @property (assign, nonatomic) CGFloat labelLeftInset; /** 显示刷新状态的label */ @property (weak, nonatomic, readonly) UILabel *stateLabel; /** 设置state状态下的文字 */ - (void)setTitle:(NSString *)title forState:(MJRefreshState)state; /** 获取state状态下的title */ - (NSString *)titleForState:(MJRefreshState)state; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m ================================================ // // MJRefreshBackStateFooter.m // MJRefreshExample // // Created by MJ Lee on 15/6/13. // Copyright © 2015年 小码哥. All rights reserved. // #import "MJRefreshBackStateFooter.h" @interface MJRefreshBackStateFooter() { /** 显示刷新状态的label */ __unsafe_unretained UILabel *_stateLabel; } /** 所有状态对应的文字 */ @property (strong, nonatomic) NSMutableDictionary *stateTitles; @end @implementation MJRefreshBackStateFooter #pragma mark - 懒加载 - (NSMutableDictionary *)stateTitles { if (!_stateTitles) { self.stateTitles = [NSMutableDictionary dictionary]; } return _stateTitles; } - (UILabel *)stateLabel { if (!_stateLabel) { [self addSubview:_stateLabel = [UILabel mj_label]]; } return _stateLabel; } #pragma mark - 公共方法 - (void)setTitle:(NSString *)title forState:(MJRefreshState)state { if (title == nil) return; self.stateTitles[@(state)] = title; self.stateLabel.text = self.stateTitles[@(self.state)]; } - (NSString *)titleForState:(MJRefreshState)state { return self.stateTitles[@(state)]; } #pragma mark - 重写父类的方法 - (void)prepare { [super prepare]; // 初始化间距 self.labelLeftInset = MJRefreshLabelLeftInset; // 初始化文字 [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterIdleText] forState:MJRefreshStateIdle]; [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterPullingText] forState:MJRefreshStatePulling]; [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterRefreshingText] forState:MJRefreshStateRefreshing]; [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterNoMoreDataText] forState:MJRefreshStateNoMoreData]; } - (void)placeSubviews { [super placeSubviews]; if (self.stateLabel.constraints.count) return; // 状态标签 self.stateLabel.frame = self.bounds; } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 设置状态文字 self.stateLabel.text = self.stateTitles[@(state)]; } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h ================================================ // // MJRefreshGifHeader.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshStateHeader.h" @interface MJRefreshGifHeader : MJRefreshStateHeader @property (weak, nonatomic, readonly) UIImageView *gifView; /** 设置state状态下的动画图片images 动画持续时间duration*/ - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; - (void)setImages:(NSArray *)images forState:(MJRefreshState)state; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m ================================================ // // MJRefreshGifHeader.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshGifHeader.h" @interface MJRefreshGifHeader() { __unsafe_unretained UIImageView *_gifView; } /** 所有状态对应的动画图片 */ @property (strong, nonatomic) NSMutableDictionary *stateImages; /** 所有状态对应的动画时间 */ @property (strong, nonatomic) NSMutableDictionary *stateDurations; @end @implementation MJRefreshGifHeader #pragma mark - 懒加载 - (UIImageView *)gifView { if (!_gifView) { UIImageView *gifView = [[UIImageView alloc] init]; [self addSubview:_gifView = gifView]; } return _gifView; } - (NSMutableDictionary *)stateImages { if (!_stateImages) { self.stateImages = [NSMutableDictionary dictionary]; } return _stateImages; } - (NSMutableDictionary *)stateDurations { if (!_stateDurations) { self.stateDurations = [NSMutableDictionary dictionary]; } return _stateDurations; } #pragma mark - 公共方法 - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state { if (images == nil) return; self.stateImages[@(state)] = images; self.stateDurations[@(state)] = @(duration); /* 根据图片设置控件的高度 */ UIImage *image = [images firstObject]; if (image.size.height > self.mj_h) { self.mj_h = image.size.height; } } - (void)setImages:(NSArray *)images forState:(MJRefreshState)state { [self setImages:images duration:images.count * 0.1 forState:state]; } #pragma mark - 实现父类的方法 - (void)prepare { [super prepare]; // 初始化间距 self.labelLeftInset = 20; } - (void)setPullingPercent:(CGFloat)pullingPercent { [super setPullingPercent:pullingPercent]; NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; if (self.state != MJRefreshStateIdle || images.count == 0) return; // 停止动画 [self.gifView stopAnimating]; // 设置当前需要显示的图片 NSUInteger index = images.count * pullingPercent; if (index >= images.count) index = images.count - 1; self.gifView.image = images[index]; } - (void)placeSubviews { [super placeSubviews]; if (self.gifView.constraints.count) return; self.gifView.frame = self.bounds; if (self.stateLabel.hidden && self.lastUpdatedTimeLabel.hidden) { self.gifView.contentMode = UIViewContentModeCenter; } else { self.gifView.contentMode = UIViewContentModeRight; CGFloat stateWidth = self.stateLabel.mj_textWith; CGFloat timeWidth = 0.0; if (!self.lastUpdatedTimeLabel.hidden) { timeWidth = self.lastUpdatedTimeLabel.mj_textWith; } CGFloat textWidth = MAX(stateWidth, timeWidth); self.gifView.mj_w = self.mj_w * 0.5 - textWidth * 0.5 - self.labelLeftInset; } } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态做事情 if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { NSArray *images = self.stateImages[@(state)]; if (images.count == 0) return; [self.gifView stopAnimating]; if (images.count == 1) { // 单张图片 self.gifView.image = [images lastObject]; } else { // 多张图片 self.gifView.animationImages = images; self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; [self.gifView startAnimating]; } } else if (state == MJRefreshStateIdle) { [self.gifView stopAnimating]; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h ================================================ // // MJRefreshNormalHeader.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshStateHeader.h" @interface MJRefreshNormalHeader : MJRefreshStateHeader @property (weak, nonatomic, readonly) UIImageView *arrowView; /** 菊花的样式 */ @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m ================================================ // // MJRefreshNormalHeader.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshNormalHeader.h" #import "NSBundle+MJRefresh.h" @interface MJRefreshNormalHeader() { __unsafe_unretained UIImageView *_arrowView; } @property (weak, nonatomic) UIActivityIndicatorView *loadingView; @end @implementation MJRefreshNormalHeader #pragma mark - 懒加载子控件 - (UIImageView *)arrowView { if (!_arrowView) { UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]]; [self addSubview:_arrowView = arrowView]; } return _arrowView; } - (UIActivityIndicatorView *)loadingView { if (!_loadingView) { UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; loadingView.hidesWhenStopped = YES; [self addSubview:_loadingView = loadingView]; } return _loadingView; } #pragma mark - 公共方法 - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle { _activityIndicatorViewStyle = activityIndicatorViewStyle; self.loadingView = nil; [self setNeedsLayout]; } #pragma mark - 重写父类的方法 - (void)prepare { [super prepare]; self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; } - (void)placeSubviews { [super placeSubviews]; // 箭头的中心点 CGFloat arrowCenterX = self.mj_w * 0.5; if (!self.stateLabel.hidden) { CGFloat stateWidth = self.stateLabel.mj_textWith; CGFloat timeWidth = 0.0; if (!self.lastUpdatedTimeLabel.hidden) { timeWidth = self.lastUpdatedTimeLabel.mj_textWith; } CGFloat textWidth = MAX(stateWidth, timeWidth); arrowCenterX -= textWidth / 2 + self.labelLeftInset; } CGFloat arrowCenterY = self.mj_h * 0.5; CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); // 箭头 if (self.arrowView.constraints.count == 0) { self.arrowView.mj_size = self.arrowView.image.size; self.arrowView.center = arrowCenter; } // 圈圈 if (self.loadingView.constraints.count == 0) { self.loadingView.center = arrowCenter; } self.arrowView.tintColor = self.stateLabel.textColor; } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 根据状态做事情 if (state == MJRefreshStateIdle) { if (oldState == MJRefreshStateRefreshing) { self.arrowView.transform = CGAffineTransformIdentity; [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ self.loadingView.alpha = 0.0; } completion:^(BOOL finished) { // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态 if (self.state != MJRefreshStateIdle) return; self.loadingView.alpha = 1.0; [self.loadingView stopAnimating]; self.arrowView.hidden = NO; }]; } else { [self.loadingView stopAnimating]; self.arrowView.hidden = NO; [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ self.arrowView.transform = CGAffineTransformIdentity; }]; } } else if (state == MJRefreshStatePulling) { [self.loadingView stopAnimating]; self.arrowView.hidden = NO; [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); }]; } else if (state == MJRefreshStateRefreshing) { self.loadingView.alpha = 1.0; // 防止refreshing -> idle的动画完毕动作没有被执行 [self.loadingView startAnimating]; self.arrowView.hidden = YES; } } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h ================================================ // // MJRefreshStateHeader.h // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshHeader.h" @interface MJRefreshStateHeader : MJRefreshHeader #pragma mark - 刷新时间相关 /** 利用这个block来决定显示的更新时间文字 */ @property (copy, nonatomic) NSString *(^lastUpdatedTimeText)(NSDate *lastUpdatedTime); /** 显示上一次刷新时间的label */ @property (weak, nonatomic, readonly) UILabel *lastUpdatedTimeLabel; #pragma mark - 状态相关 /** 文字距离圈圈、箭头的距离 */ @property (assign, nonatomic) CGFloat labelLeftInset; /** 显示刷新状态的label */ @property (weak, nonatomic, readonly) UILabel *stateLabel; /** 设置state状态下的文字 */ - (void)setTitle:(NSString *)title forState:(MJRefreshState)state; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m ================================================ // // MJRefreshStateHeader.m // MJRefreshExample // // Created by MJ Lee on 15/4/24. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "MJRefreshStateHeader.h" @interface MJRefreshStateHeader() { /** 显示上一次刷新时间的label */ __unsafe_unretained UILabel *_lastUpdatedTimeLabel; /** 显示刷新状态的label */ __unsafe_unretained UILabel *_stateLabel; } /** 所有状态对应的文字 */ @property (strong, nonatomic) NSMutableDictionary *stateTitles; @end @implementation MJRefreshStateHeader #pragma mark - 懒加载 - (NSMutableDictionary *)stateTitles { if (!_stateTitles) { self.stateTitles = [NSMutableDictionary dictionary]; } return _stateTitles; } - (UILabel *)stateLabel { if (!_stateLabel) { [self addSubview:_stateLabel = [UILabel mj_label]]; } return _stateLabel; } - (UILabel *)lastUpdatedTimeLabel { if (!_lastUpdatedTimeLabel) { [self addSubview:_lastUpdatedTimeLabel = [UILabel mj_label]]; } return _lastUpdatedTimeLabel; } #pragma mark - 公共方法 - (void)setTitle:(NSString *)title forState:(MJRefreshState)state { if (title == nil) return; self.stateTitles[@(state)] = title; self.stateLabel.text = self.stateTitles[@(self.state)]; } #pragma mark - 日历获取在9.x之后的系统使用currentCalendar会出异常。在8.0之后使用系统新API。 - (NSCalendar *)currentCalendar { if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) { return [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; } return [NSCalendar currentCalendar]; } #pragma mark key的处理 - (void)setLastUpdatedTimeKey:(NSString *)lastUpdatedTimeKey { [super setLastUpdatedTimeKey:lastUpdatedTimeKey]; // 如果label隐藏了,就不用再处理 if (self.lastUpdatedTimeLabel.hidden) return; NSDate *lastUpdatedTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastUpdatedTimeKey]; // 如果有block if (self.lastUpdatedTimeText) { self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime); return; } if (lastUpdatedTime) { // 1.获得年月日 NSCalendar *calendar = [self currentCalendar]; NSUInteger unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitHour |NSCalendarUnitMinute; NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:lastUpdatedTime]; NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]]; // 2.格式化日期 NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; BOOL isToday = NO; if ([cmp1 day] == [cmp2 day]) { // 今天 formatter.dateFormat = @" HH:mm"; isToday = YES; } else if ([cmp1 year] == [cmp2 year]) { // 今年 formatter.dateFormat = @"MM-dd HH:mm"; } else { formatter.dateFormat = @"yyyy-MM-dd HH:mm"; } NSString *time = [formatter stringFromDate:lastUpdatedTime]; // 3.显示日期 self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@%@", [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText], isToday ? [NSBundle mj_localizedStringForKey:MJRefreshHeaderDateTodayText] : @"", time]; } else { self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@", [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText], [NSBundle mj_localizedStringForKey:MJRefreshHeaderNoneLastDateText]]; } } #pragma mark - 覆盖父类的方法 - (void)prepare { [super prepare]; // 初始化间距 self.labelLeftInset = MJRefreshLabelLeftInset; // 初始化文字 [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderIdleText] forState:MJRefreshStateIdle]; [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderPullingText] forState:MJRefreshStatePulling]; [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderRefreshingText] forState:MJRefreshStateRefreshing]; } - (void)placeSubviews { [super placeSubviews]; if (self.stateLabel.hidden) return; BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0; if (self.lastUpdatedTimeLabel.hidden) { // 状态 if (noConstrainsOnStatusLabel) { self.stateLabel.frame = self.bounds; } } else { CGFloat stateLabelH = self.mj_h * 0.5; // 状态 if (noConstrainsOnStatusLabel) { self.stateLabel.mj_x = 0; self.stateLabel.mj_y = 0; self.stateLabel.mj_w = self.mj_w; self.stateLabel.mj_h = stateLabelH; } // 更新时间 if (self.lastUpdatedTimeLabel.constraints.count == 0) { self.lastUpdatedTimeLabel.mj_x = 0; self.lastUpdatedTimeLabel.mj_y = stateLabelH; self.lastUpdatedTimeLabel.mj_w = self.mj_w; self.lastUpdatedTimeLabel.mj_h = self.mj_h - self.lastUpdatedTimeLabel.mj_y; } } } - (void)setState:(MJRefreshState)state { MJRefreshCheckState // 设置状态文字 self.stateLabel.text = self.stateTitles[@(state)]; // 重新设置key(重新显示时间) self.lastUpdatedTimeKey = self.lastUpdatedTimeKey; } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hant.lproj/Localizable.strings ================================================ "MJRefreshHeaderIdleText" = "下拉可以刷新"; "MJRefreshHeaderPullingText" = "鬆開立即刷新"; "MJRefreshHeaderRefreshingText" = "正在刷新數據中..."; "MJRefreshAutoFooterIdleText" = "點擊或上拉加載更多"; "MJRefreshAutoFooterRefreshingText" = "正在加載更多的數據..."; "MJRefreshAutoFooterNoMoreDataText" = "已經全部加載完畢"; "MJRefreshBackFooterIdleText" = "上拉可以加載更多"; "MJRefreshBackFooterPullingText" = "鬆開立即加載更多"; "MJRefreshBackFooterRefreshingText" = "正在加載更多的數據..."; "MJRefreshBackFooterNoMoreDataText" = "已經全部加載完畢"; "MJRefreshHeaderLastTimeText" = "最後更新:"; "MJRefreshHeaderDateTodayText" = "今天"; "MJRefreshHeaderNoneLastDateText" = "無記錄"; ================================================ FILE: Pods/MJRefresh/MJRefresh/MJRefresh.h ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 #import "UIScrollView+MJRefresh.h" #import "UIScrollView+MJExtension.h" #import "UIView+MJExtension.h" #import "MJRefreshNormalHeader.h" #import "MJRefreshGifHeader.h" #import "MJRefreshBackNormalFooter.h" #import "MJRefreshBackGifFooter.h" #import "MJRefreshAutoNormalFooter.h" #import "MJRefreshAutoGifFooter.h" ================================================ FILE: Pods/MJRefresh/MJRefresh/MJRefreshConst.h ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 #import #import // 弱引用 #define MJWeakSelf __weak typeof(self) weakSelf = self; // 日志输出 #ifdef DEBUG #define MJRefreshLog(...) NSLog(__VA_ARGS__) #else #define MJRefreshLog(...) #endif // 过期提醒 #define MJRefreshDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) // 运行时objc_msgSend #define MJRefreshMsgSend(...) ((void (*)(void *, SEL, UIView *))objc_msgSend)(__VA_ARGS__) #define MJRefreshMsgTarget(target) (__bridge void *)(target) // RGB颜色 #define MJRefreshColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] // 文字颜色 #define MJRefreshLabelTextColor MJRefreshColor(90, 90, 90) // 字体大小 #define MJRefreshLabelFont [UIFont boldSystemFontOfSize:14] // 常量 UIKIT_EXTERN const CGFloat MJRefreshLabelLeftInset; UIKIT_EXTERN const CGFloat MJRefreshHeaderHeight; UIKIT_EXTERN const CGFloat MJRefreshFooterHeight; UIKIT_EXTERN const CGFloat MJRefreshFastAnimationDuration; UIKIT_EXTERN const CGFloat MJRefreshSlowAnimationDuration; UIKIT_EXTERN NSString *const MJRefreshKeyPathContentOffset; UIKIT_EXTERN NSString *const MJRefreshKeyPathContentSize; UIKIT_EXTERN NSString *const MJRefreshKeyPathContentInset; UIKIT_EXTERN NSString *const MJRefreshKeyPathPanState; UIKIT_EXTERN NSString *const MJRefreshHeaderLastUpdatedTimeKey; UIKIT_EXTERN NSString *const MJRefreshHeaderIdleText; UIKIT_EXTERN NSString *const MJRefreshHeaderPullingText; UIKIT_EXTERN NSString *const MJRefreshHeaderRefreshingText; UIKIT_EXTERN NSString *const MJRefreshAutoFooterIdleText; UIKIT_EXTERN NSString *const MJRefreshAutoFooterRefreshingText; UIKIT_EXTERN NSString *const MJRefreshAutoFooterNoMoreDataText; UIKIT_EXTERN NSString *const MJRefreshBackFooterIdleText; UIKIT_EXTERN NSString *const MJRefreshBackFooterPullingText; UIKIT_EXTERN NSString *const MJRefreshBackFooterRefreshingText; UIKIT_EXTERN NSString *const MJRefreshBackFooterNoMoreDataText; UIKIT_EXTERN NSString *const MJRefreshHeaderLastTimeText; UIKIT_EXTERN NSString *const MJRefreshHeaderDateTodayText; UIKIT_EXTERN NSString *const MJRefreshHeaderNoneLastDateText; // 状态检查 #define MJRefreshCheckState \ MJRefreshState oldState = self.state; \ if (state == oldState) return; \ [super setState:state]; ================================================ FILE: Pods/MJRefresh/MJRefresh/MJRefreshConst.m ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 #import const CGFloat MJRefreshLabelLeftInset = 25; const CGFloat MJRefreshHeaderHeight = 54.0; const CGFloat MJRefreshFooterHeight = 44.0; const CGFloat MJRefreshFastAnimationDuration = 0.25; const CGFloat MJRefreshSlowAnimationDuration = 0.4; NSString *const MJRefreshKeyPathContentOffset = @"contentOffset"; NSString *const MJRefreshKeyPathContentInset = @"contentInset"; NSString *const MJRefreshKeyPathContentSize = @"contentSize"; NSString *const MJRefreshKeyPathPanState = @"state"; NSString *const MJRefreshHeaderLastUpdatedTimeKey = @"MJRefreshHeaderLastUpdatedTimeKey"; NSString *const MJRefreshHeaderIdleText = @"MJRefreshHeaderIdleText"; NSString *const MJRefreshHeaderPullingText = @"MJRefreshHeaderPullingText"; NSString *const MJRefreshHeaderRefreshingText = @"MJRefreshHeaderRefreshingText"; NSString *const MJRefreshAutoFooterIdleText = @"MJRefreshAutoFooterIdleText"; NSString *const MJRefreshAutoFooterRefreshingText = @"MJRefreshAutoFooterRefreshingText"; NSString *const MJRefreshAutoFooterNoMoreDataText = @"MJRefreshAutoFooterNoMoreDataText"; NSString *const MJRefreshBackFooterIdleText = @"MJRefreshBackFooterIdleText"; NSString *const MJRefreshBackFooterPullingText = @"MJRefreshBackFooterPullingText"; NSString *const MJRefreshBackFooterRefreshingText = @"MJRefreshBackFooterRefreshingText"; NSString *const MJRefreshBackFooterNoMoreDataText = @"MJRefreshBackFooterNoMoreDataText"; NSString *const MJRefreshHeaderLastTimeText = @"MJRefreshHeaderLastTimeText"; NSString *const MJRefreshHeaderDateTodayText = @"MJRefreshHeaderDateTodayText"; NSString *const MJRefreshHeaderNoneLastDateText = @"MJRefreshHeaderNoneLastDateText"; ================================================ FILE: Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.h ================================================ // // NSBundle+MJRefresh.h // MJRefreshExample // // Created by MJ Lee on 16/6/13. // Copyright © 2016年 小码哥. All rights reserved. // #import @interface NSBundle (MJRefresh) + (instancetype)mj_refreshBundle; + (UIImage *)mj_arrowImage; + (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value; + (NSString *)mj_localizedStringForKey:(NSString *)key; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.m ================================================ // // NSBundle+MJRefresh.m // MJRefreshExample // // Created by MJ Lee on 16/6/13. // Copyright © 2016年 小码哥. All rights reserved. // #import "NSBundle+MJRefresh.h" #import "MJRefreshComponent.h" @implementation NSBundle (MJRefresh) + (instancetype)mj_refreshBundle { static NSBundle *refreshBundle = nil; if (refreshBundle == nil) { // 这里不使用mainBundle是为了适配pod 1.x和0.x refreshBundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[MJRefreshComponent class]] pathForResource:@"MJRefresh" ofType:@"bundle"]]; } return refreshBundle; } + (UIImage *)mj_arrowImage { static UIImage *arrowImage = nil; if (arrowImage == nil) { arrowImage = [[UIImage imageWithContentsOfFile:[[self mj_refreshBundle] pathForResource:@"arrow@2x" ofType:@"png"]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; } return arrowImage; } + (NSString *)mj_localizedStringForKey:(NSString *)key { return [self mj_localizedStringForKey:key value:nil]; } + (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value { static NSBundle *bundle = nil; if (bundle == nil) { // (iOS获取的语言字符串比较不稳定)目前框架只处理en、zh-Hans、zh-Hant三种情况,其他按照系统默认处理 NSString *language = [NSLocale preferredLanguages].firstObject; if ([language hasPrefix:@"en"]) { language = @"en"; } else if ([language hasPrefix:@"zh"]) { if ([language rangeOfString:@"Hans"].location != NSNotFound) { language = @"zh-Hans"; // 简体中文 } else { // zh-Hant\zh-HK\zh-TW language = @"zh-Hant"; // 繁體中文 } } else { language = @"en"; } // 从MJRefresh.bundle中查找资源 bundle = [NSBundle bundleWithPath:[[NSBundle mj_refreshBundle] pathForResource:language ofType:@"lproj"]]; } value = [bundle localizedStringForKey:key value:value table:nil]; return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil]; } @end ================================================ FILE: Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // UIScrollView+Extension.h // MJRefreshExample // // Created by MJ Lee on 14-5-28. // Copyright (c) 2014年 小码哥. All rights reserved. // #import @interface UIScrollView (MJExtension) @property (readonly, nonatomic) UIEdgeInsets mj_inset; @property (assign, nonatomic) CGFloat mj_insetT; @property (assign, nonatomic) CGFloat mj_insetB; @property (assign, nonatomic) CGFloat mj_insetL; @property (assign, nonatomic) CGFloat mj_insetR; @property (assign, nonatomic) CGFloat mj_offsetX; @property (assign, nonatomic) CGFloat mj_offsetY; @property (assign, nonatomic) CGFloat mj_contentW; @property (assign, nonatomic) CGFloat mj_contentH; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // UIScrollView+Extension.m // MJRefreshExample // // Created by MJ Lee on 14-5-28. // Copyright (c) 2014年 小码哥. All rights reserved. // #import "UIScrollView+MJExtension.h" #import #define SYSTEM_VERSION_GREATER_NOT_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability-new" @implementation UIScrollView (MJExtension) - (UIEdgeInsets)mj_inset { #ifdef __IPHONE_11_0 if (SYSTEM_VERSION_GREATER_NOT_LESS_THAN(@"11.0")) { return self.adjustedContentInset; } #endif return self.contentInset; } - (void)setMj_insetT:(CGFloat)mj_insetT { UIEdgeInsets inset = self.contentInset; inset.top = mj_insetT; #ifdef __IPHONE_11_0 if (SYSTEM_VERSION_GREATER_NOT_LESS_THAN(@"11.0")) { inset.top -= (self.adjustedContentInset.top - self.contentInset.top); } #endif self.contentInset = inset; } - (CGFloat)mj_insetT { return self.mj_inset.top; } - (void)setMj_insetB:(CGFloat)mj_insetB { UIEdgeInsets inset = self.contentInset; inset.bottom = mj_insetB; #ifdef __IPHONE_11_0 if (SYSTEM_VERSION_GREATER_NOT_LESS_THAN(@"11.0")) { inset.bottom -= (self.adjustedContentInset.bottom - self.contentInset.bottom); } #endif self.contentInset = inset; } - (CGFloat)mj_insetB { return self.mj_inset.bottom; } - (void)setMj_insetL:(CGFloat)mj_insetL { UIEdgeInsets inset = self.contentInset; inset.left = mj_insetL; #ifdef __IPHONE_11_0 if (SYSTEM_VERSION_GREATER_NOT_LESS_THAN(@"11.0")) { inset.left -= (self.adjustedContentInset.left - self.contentInset.left); } #endif self.contentInset = inset; } - (CGFloat)mj_insetL { return self.mj_inset.left; } - (void)setMj_insetR:(CGFloat)mj_insetR { UIEdgeInsets inset = self.contentInset; inset.right = mj_insetR; #ifdef __IPHONE_11_0 if (SYSTEM_VERSION_GREATER_NOT_LESS_THAN(@"11.0")) { inset.right -= (self.adjustedContentInset.right - self.contentInset.right); } #endif self.contentInset = inset; } - (CGFloat)mj_insetR { return self.mj_inset.right; } - (void)setMj_offsetX:(CGFloat)mj_offsetX { CGPoint offset = self.contentOffset; offset.x = mj_offsetX; self.contentOffset = offset; } - (CGFloat)mj_offsetX { return self.contentOffset.x; } - (void)setMj_offsetY:(CGFloat)mj_offsetY { CGPoint offset = self.contentOffset; offset.y = mj_offsetY; self.contentOffset = offset; } - (CGFloat)mj_offsetY { return self.contentOffset.y; } - (void)setMj_contentW:(CGFloat)mj_contentW { CGSize size = self.contentSize; size.width = mj_contentW; self.contentSize = size; } - (CGFloat)mj_contentW { return self.contentSize.width; } - (void)setMj_contentH:(CGFloat)mj_contentH { CGSize size = self.contentSize; size.height = mj_contentH; self.contentSize = size; } - (CGFloat)mj_contentH { return self.contentSize.height; } @end #pragma clang diagnostic pop ================================================ FILE: Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // UIScrollView+MJRefresh.h // MJRefreshExample // // Created by MJ Lee on 15/3/4. // Copyright (c) 2015年 小码哥. All rights reserved. // 给ScrollView增加下拉刷新、上拉刷新的功能 #import #import "MJRefreshConst.h" @class MJRefreshHeader, MJRefreshFooter; @interface UIScrollView (MJRefresh) /** 下拉刷新控件 */ @property (strong, nonatomic) MJRefreshHeader *mj_header; @property (strong, nonatomic) MJRefreshHeader *header MJRefreshDeprecated("使用mj_header"); /** 上拉刷新控件 */ @property (strong, nonatomic) MJRefreshFooter *mj_footer; @property (strong, nonatomic) MJRefreshFooter *footer MJRefreshDeprecated("使用mj_footer"); #pragma mark - other - (NSInteger)mj_totalDataCount; @property (copy, nonatomic) void (^mj_reloadDataBlock)(NSInteger totalDataCount); @end ================================================ FILE: Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // UIScrollView+MJRefresh.m // MJRefreshExample // // Created by MJ Lee on 15/3/4. // Copyright (c) 2015年 小码哥. All rights reserved. // #import "UIScrollView+MJRefresh.h" #import "MJRefreshHeader.h" #import "MJRefreshFooter.h" #import @implementation NSObject (MJRefresh) + (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2 { method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2)); } + (void)exchangeClassMethod1:(SEL)method1 method2:(SEL)method2 { method_exchangeImplementations(class_getClassMethod(self, method1), class_getClassMethod(self, method2)); } @end @implementation UIScrollView (MJRefresh) #pragma mark - header static const char MJRefreshHeaderKey = '\0'; - (void)setMj_header:(MJRefreshHeader *)mj_header { if (mj_header != self.mj_header) { // 删除旧的,添加新的 [self.mj_header removeFromSuperview]; [self insertSubview:mj_header atIndex:0]; // 存储新的 [self willChangeValueForKey:@"mj_header"]; // KVO objc_setAssociatedObject(self, &MJRefreshHeaderKey, mj_header, OBJC_ASSOCIATION_ASSIGN); [self didChangeValueForKey:@"mj_header"]; // KVO } } - (MJRefreshHeader *)mj_header { return objc_getAssociatedObject(self, &MJRefreshHeaderKey); } #pragma mark - footer static const char MJRefreshFooterKey = '\0'; - (void)setMj_footer:(MJRefreshFooter *)mj_footer { if (mj_footer != self.mj_footer) { // 删除旧的,添加新的 [self.mj_footer removeFromSuperview]; [self insertSubview:mj_footer atIndex:0]; // 存储新的 [self willChangeValueForKey:@"mj_footer"]; // KVO objc_setAssociatedObject(self, &MJRefreshFooterKey, mj_footer, OBJC_ASSOCIATION_ASSIGN); [self didChangeValueForKey:@"mj_footer"]; // KVO } } - (MJRefreshFooter *)mj_footer { return objc_getAssociatedObject(self, &MJRefreshFooterKey); } #pragma mark - 过期 - (void)setFooter:(MJRefreshFooter *)footer { self.mj_footer = footer; } - (MJRefreshFooter *)footer { return self.mj_footer; } - (void)setHeader:(MJRefreshHeader *)header { self.mj_header = header; } - (MJRefreshHeader *)header { return self.mj_header; } #pragma mark - other - (NSInteger)mj_totalDataCount { NSInteger totalCount = 0; if ([self isKindOfClass:[UITableView class]]) { UITableView *tableView = (UITableView *)self; for (NSInteger section = 0; section @interface UIView (MJExtension) @property (assign, nonatomic) CGFloat mj_x; @property (assign, nonatomic) CGFloat mj_y; @property (assign, nonatomic) CGFloat mj_w; @property (assign, nonatomic) CGFloat mj_h; @property (assign, nonatomic) CGSize mj_size; @property (assign, nonatomic) CGPoint mj_origin; @end ================================================ FILE: Pods/MJRefresh/MJRefresh/UIView+MJExtension.m ================================================ // 代码地址: https://github.com/CoderMJLee/MJRefresh // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 // UIView+Extension.m // MJRefreshExample // // Created by MJ Lee on 14-5-28. // Copyright (c) 2014年 小码哥. All rights reserved. // #import "UIView+MJExtension.h" @implementation UIView (MJExtension) - (void)setMj_x:(CGFloat)mj_x { CGRect frame = self.frame; frame.origin.x = mj_x; self.frame = frame; } - (CGFloat)mj_x { return self.frame.origin.x; } - (void)setMj_y:(CGFloat)mj_y { CGRect frame = self.frame; frame.origin.y = mj_y; self.frame = frame; } - (CGFloat)mj_y { return self.frame.origin.y; } - (void)setMj_w:(CGFloat)mj_w { CGRect frame = self.frame; frame.size.width = mj_w; self.frame = frame; } - (CGFloat)mj_w { return self.frame.size.width; } - (void)setMj_h:(CGFloat)mj_h { CGRect frame = self.frame; frame.size.height = mj_h; self.frame = frame; } - (CGFloat)mj_h { return self.frame.size.height; } - (void)setMj_size:(CGSize)mj_size { CGRect frame = self.frame; frame.size = mj_size; self.frame = frame; } - (CGSize)mj_size { return self.frame.size; } - (void)setMj_origin:(CGPoint)mj_origin { CGRect frame = self.frame; frame.origin = mj_origin; self.frame = frame; } - (CGPoint)mj_origin { return self.frame.origin; } @end ================================================ FILE: Pods/MJRefresh/README.md ================================================ ![(logo)](http://images.cnitblog.com/blog2015/497279/201505/051004492043385.png) ## MJRefresh * An easy way to use pull-to-refresh ## Contents * Getting Started * [Features【Support what kinds of controls to refresh】](#Support_what_kinds_of_controls_to_refresh) * [Installation【How to use MJRefresh】](#How_to_use_MJRefresh) * [Who's using【More than hundreds of Apps are using MJRefresh】](#More_than_hundreds_of_Apps_are_using_MJRefresh) * [Classes【The Class Structure Chart of MJRefresh】](#The_Class_Structure_Chart_of_MJRefresh) * Comment API * [MJRefreshComponent.h](#MJRefreshComponent.h) * [MJRefreshHeader.h](#MJRefreshHeader.h) * [MJRefreshFooter.h](#MJRefreshFooter.h) * [MJRefreshAutoFooter.h](#MJRefreshAutoFooter.h) * Examples * [Reference](#Reference) * [The drop-down refresh 01-Default](#The_drop-down_refresh_01-Default) * [The drop-down refresh 02-Animation image](#The_drop-down_refresh_02-Animation_image) * [The drop-down refresh 03-Hide the time](#The_drop-down_refresh_03-Hide_the_time) * [The drop-down refresh 04-Hide status and time](#The_drop-down_refresh_04-Hide_status_and_time) * [The drop-down refresh 05-DIY title](#The_drop-down_refresh_05-DIY_title) * [The drop-down refresh 06-DIY the control of refresh](#The_drop-down_refresh_06-DIY_the_control_of_refresh) * [The pull to refresh 01-Default](#The_pull_to_refresh_01-Default) * [The pull to refresh 02-Animation image](#The_pull_to_refresh_02-Animation_image) * [The pull to refresh 03-Hide the title of refresh status](#The_pull_to_refresh_03-Hide_the_title_of_refresh_status) * [The pull to refresh 04-All loaded](#The_pull_to_refresh_04-All_loaded) * [The pull to refresh 05-DIY title](#The_pull_to_refresh_05-DIY_title) * [The pull to refresh 06-Hidden After loaded](#The_pull_to_refresh_06-Hidden_After_loaded) * [The pull to refresh 07-Automatic back of the pull01](#The_pull_to_refresh_07-Automatic_back_of_the_pull01) * [The pull to refresh 08-Automatic back of the pull02](#The_pull_to_refresh_08-Automatic_back_of_the_pull02) * [The pull to refresh 09-DIY the control of refresh(Automatic refresh)](#The_pull_to_refresh_09-DIY_the_control_of_refresh(Automatic_refresh)) * [The pull to refresh 10-DIY the control of refresh(Automatic back)](#The_pull_to_refresh_10-DIY_the_control_of_refresh(Automatic_back)) * [UICollectionView01-The pull and drop-down refresh](#UICollectionView01-The_pull_and_drop-down_refresh) * [UIWebView01-The drop-down refresh](#UIWebView01-The_drop-down_refresh) * [Hope](#Hope) ## Support what kinds of controls to refresh * `UIScrollView`、`UITableView`、`UICollectionView`、`UIWebView` ## How to use MJRefresh * Installation with CocoaPods:`pod 'MJRefresh'` * Manual import: * Drag All files in the `MJRefresh` folder to project * Import the main file:`#import "MJRefresh.h"` ```objc Base Custom MJRefresh.bundle MJRefresh.h MJRefreshConst.h MJRefreshConst.m UIScrollView+MJExtension.h UIScrollView+MJExtension.m UIScrollView+MJRefresh.h UIScrollView+MJRefresh.m UIView+MJExtension.h UIView+MJExtension.m ``` ## More than hundreds of Apps are using MJRefresh * More information of App can focus on:[M了个J-博客园](http://www.cnblogs.com/mjios/p/4409853.html) ## The Class Structure Chart of MJRefresh ![](http://images0.cnblogs.com/blog2015/497279/201506/132232456139177.png) - `The class of red text` in the chart:You can use them directly - The drop-down refresh control types - Normal:`MJRefreshNormalHeader` - Gif:`MJRefreshGifHeader` - The pull to refresh control types - Auto refresh - Normal:`MJRefreshAutoNormalFooter` - Gif:`MJRefreshAutoGifFooter` - Auto Back - Normal:`MJRefreshBackNormalFooter` - Gif:`MJRefreshBackGifFooter` - `The class of non-red text` in the chart:For inheritance,to use DIY the control of refresh - About how to DIY the control of refresh,You can refer the Class in below Chart
## MJRefreshComponent.h ```objc /** The Base Class of refresh control */ @interface MJRefreshComponent : UIView #pragma mark - Control the state of Refresh /** BeginRefreshing */ - (void)beginRefreshing; /** EndRefreshing */ - (void)endRefreshing; /** IsRefreshing */ - (BOOL)isRefreshing; #pragma mark - Other /** According to the drag ratio to change alpha automatically */ @property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha; @end ``` ## MJRefreshHeader.h ```objc @interface MJRefreshHeader : MJRefreshComponent /** Creat header */ + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; /** Creat header */ + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; /** This key is used to storage the time that the last time of drown-down successfully */ @property (copy, nonatomic) NSString *lastUpdatedTimeKey; /** The last time of drown-down successfully */ @property (strong, nonatomic, readonly) NSDate *lastUpdatedTime; /** Ignored scrollView contentInset top */ @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop; @end ``` ## MJRefreshFooter.h ```objc @interface MJRefreshFooter : MJRefreshComponent /** Creat footer */ + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; /** Creat footer */ + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; /** NoticeNoMoreData */ - (void)noticeNoMoreData; /** ResetNoMoreData(Clear the status of NoMoreData ) */ - (void)resetNoMoreData; /** Ignored scrollView contentInset bottom */ @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom; /** Automaticlly show or hidden by the count of data(Show-have data,Hidden- no data) */ @property (assign, nonatomic) BOOL automaticallyHidden; @end ``` ## MJRefreshAutoFooter.h ```objc @interface MJRefreshAutoFooter : MJRefreshFooter /** Is Automatically Refresh(Default is Yes) */ @property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh; /** When there is much at the bottom of the control is automatically refresh(Default is 1.0,Is at the bottom of the control appears in full, will refresh automatically) */ @property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent; @end ``` ## Reference ```objc * Due to there are more functions of this framework,Don't write specific text describe its usage * You can directly reference examples MJTableViewController、MJCollectionViewController、MJWebViewController,More intuitive and fast. ``` ## The drop-down refresh 01-Default ```objc self.tableView.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ //Call this Block When enter the refresh status automatically }]; 或 // Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadNewData]) self.tableView.header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; // Enter the refresh status immediately [self.tableView.header beginRefreshing]; ``` ![(下拉刷新01-普通)](http://images0.cnblogs.com/blog2015/497279/201506/141204343486151.gif) ## The drop-down refresh 02-Animation image ```objc // Set the callback(一Once you enter the refresh status,then call the action of target,that is call [self loadNewData]) MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; // Set the ordinary state of animated images [header setImages:idleImages forState:MJRefreshStateIdle]; // Set the pulling state of animated images(Enter the status of refreshing as soon as loosen) [header setImages:pullingImages forState:MJRefreshStatePulling]; // Set the refreshing state of animated images [header setImages:refreshingImages forState:MJRefreshStateRefreshing]; // Set header self.tableView.mj_header = header; ``` ![(下拉刷新02-动画图片)](http://images0.cnblogs.com/blog2015/497279/201506/141204402238389.gif) ## The drop-down refresh 03-Hide the time ```objc // Hide the time header.lastUpdatedTimeLabel.hidden = YES; ``` ![(下拉刷新03-隐藏时间)](http://images0.cnblogs.com/blog2015/497279/201506/141204456132944.gif) ## The drop-down refresh 04-Hide status and time ```objc // Hide the time header.lastUpdatedTimeLabel.hidden = YES; // Hide the status header.stateLabel.hidden = YES; ``` ![(下拉刷新04-隐藏状态和时间0)](http://images0.cnblogs.com/blog2015/497279/201506/141204508639539.gif) ## The drop-down refresh 05-DIY title ```objc // Set title [header setTitle:@"Pull down to refresh" forState:MJRefreshStateIdle]; [header setTitle:@"Release to refresh" forState:MJRefreshStatePulling]; [header setTitle:@"Loading ..." forState:MJRefreshStateRefreshing]; // Set font header.stateLabel.font = [UIFont systemFontOfSize:15]; header.lastUpdatedTimeLabel.font = [UIFont systemFontOfSize:14]; // Set textColor header.stateLabel.textColor = [UIColor redColor]; header.lastUpdatedTimeLabel.textColor = [UIColor blueColor]; ``` ![(下拉刷新05-自定义文字)](http://images0.cnblogs.com/blog2015/497279/201506/141204563633593.gif) ## The drop-down refresh 06-DIY the control of refresh ```objc self.tableView.mj_header = [MJDIYHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; // Implementation reference to MJDIYHeader.h和MJDIYHeader.m ``` ![(下拉刷新06-自定义刷新控件)](http://images0.cnblogs.com/blog2015/497279/201506/141205019261159.gif) ## The pull to refresh 01-Default ```objc self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ //Call this Block When enter the refresh status automatically }]; 或 // Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadMoreData]) self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; ``` ![(上拉刷新01-默认)](http://images0.cnblogs.com/blog2015/497279/201506/141205090047696.gif) ## The pull to refresh 02-Animation image ```objc // Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadMoreData]) MJRefreshAutoGifFooter *footer = [MJRefreshAutoGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; // Set the refresh image [footer setImages:refreshingImages forState:MJRefreshStateRefreshing]; // Set footer self.tableView.mj_footer = footer; ``` ![(上拉刷新02-动画图片)](http://images0.cnblogs.com/blog2015/497279/201506/141205141445793.gif) ## The pull to refresh 03-Hide the title of refresh status ```objc // Hide the title of refresh status footer.refreshingTitleHidden = YES; // If does have not above method,then use footer.stateLabel.hidden = YES; ``` ![(上拉刷新03-隐藏刷新状态的文字)](http://images0.cnblogs.com/blog2015/497279/201506/141205200985774.gif) ## The pull to refresh 04-All loaded ```objc //Become the status of NoMoreData [footer noticeNoMoreData]; ``` ![(上拉刷新04-全部加载完毕)](http://images0.cnblogs.com/blog2015/497279/201506/141205248634686.gif) ## The pull to refresh 05-DIY title ```objc // Set title [footer setTitle:@"Click or drag up to refresh" forState:MJRefreshStateIdle]; [footer setTitle:@"Loading more ..." forState:MJRefreshStateRefreshing]; [footer setTitle:@"No more data" forState:MJRefreshStateNoMoreData]; // Set font footer.stateLabel.font = [UIFont systemFontOfSize:17]; // Set textColor footer.stateLabel.textColor = [UIColor blueColor]; ``` ![(上拉刷新05-自定义文字)](http://images0.cnblogs.com/blog2015/497279/201506/141205295511153.gif) ## The pull to refresh 06-Hidden After loaded ```objc //Hidden current control of the pull to refresh self.tableView.mj_footer.hidden = YES; ``` ![(上拉刷新06-加载后隐藏)](http://images0.cnblogs.com/blog2015/497279/201506/141205343481821.gif) ## The pull to refresh 07-Automatic back of the pull01 ```objc self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; ``` ![(上拉刷新07-自动回弹的上拉01)](http://images0.cnblogs.com/blog2015/497279/201506/141205392239231.gif) ## The pull to refresh 08-Automatic back of the pull02 ```objc MJRefreshBackGifFooter *footer = [MJRefreshBackGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; // Set the normal state of the animated image [footer setImages:idleImages forState:MJRefreshStateIdle]; // Set the pulling state of animated images(Enter the status of refreshing as soon as loosen) [footer setImages:pullingImages forState:MJRefreshStatePulling]; // Set the refreshing state of animated images [footer setImages:refreshingImages forState:MJRefreshStateRefreshing]; // Set footer self.tableView.mj_footer = footer; ``` ![(上拉刷新07-自动回弹的上拉02)](http://images0.cnblogs.com/blog2015/497279/201506/141205441443628.gif) ## The pull to refresh 09-DIY the control of refresh(Automatic refresh) ```objc self.tableView.mj_footer = [MJDIYAutoFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; // Implementation reference to MJDIYAutoFooter.h和MJDIYAutoFooter.m ``` ![(上拉刷新09-自定义刷新控件(自动刷新))](http://images0.cnblogs.com/blog2015/497279/201506/141205500195866.gif) ## The pull to refresh 10-DIY the control of refresh(Automatic back) ```objc self.tableView.mj_footer = [MJDIYBackFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; // Implementation reference to MJDIYBackFooter.h和MJDIYBackFooter.m ``` ![(上拉刷新10-自定义刷新控件(自动回弹))](http://images0.cnblogs.com/blog2015/497279/201506/141205560666819.gif) ## UICollectionView01-The pull and drop-down refresh ```objc // The drop-down refresh self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ //Call this Block When enter the refresh status automatically }]; // The pull to refresh self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ //Call this Block When enter the refresh status automatically }]; ``` ![(UICollectionView01-上下拉刷新)](http://images0.cnblogs.com/blog2015/497279/201506/141206021603758.gif) ## UIWebView01-The drop-down refresh ```objc //Add the control of The drop-down refresh self.webView.scrollView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ //Call this Block When enter the refresh status automatically }]; ``` ![(UICollectionView01-上下拉刷新)](http://images0.cnblogs.com/blog2015/497279/201506/141206080514524.gif) ## Remind * ARC * iOS>=6.0 * iPhone \ iPad screen anyway ## Hope * If you find bug when used,Hope you can Issues me,Thank you or try to download the latest code of this framework to see the BUG has been fixed or not) * If you find the function is not enough when used,Hope you can Issues me,I very much to add more useful function to this framework ,Thank you ! * If you want to contribute code for MJRefresh,please Pull Requests me * If you use MJRefresh in your develop app,Hope you can go to[CocoaControls](https://www.cocoacontrols.com/controls/mjrefresh)to add the iTunes path of you app,I Will install your app,and according to the usage of many app,to be a better design and improve to MJRefresh,Thank you ! * StepO1(WeChat is just an Example,Explore“Your app name itunes”) ![(step01)](http://ww4.sinaimg.cn/mw1024/800cdf9ctw1eq0viiv5rsj20sm0ea41t.jpg) * StepO2 ![(step02)](http://ww2.sinaimg.cn/mw1024/800cdf9ctw1eq0vilejxlj20tu0me7a0.jpg) * StepO3 ![(step03)](http://ww1.sinaimg.cn/mw1024/800cdf9ctw1eq0viocpo5j20wc0dc0un.jpg) * StepO4 ![(step04)](http://ww3.sinaimg.cn/mw1024/800cdf9ctw1eq0vir137xj20si0gewgu.jpg) ================================================ FILE: Pods/Moya/License.md ================================================ The MIT License (MIT) Copyright (c) 2017 Artsy, Ash Furrow 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. ================================================ FILE: Pods/Moya/Readme.md ================================================

# Moya [![CircleCI](https://img.shields.io/circleci/project/github/Moya/Moya/master.svg)](https://circleci.com/gh/Moya/Moya/tree/master) [![codecov.io](https://codecov.io/github/Moya/Moya/coverage.svg?branch=master)](https://codecov.io/github/Moya/Moya?branch=master) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/Moya.svg)](https://cocoapods.org/pods/Moya) [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) *A Chinese version of this document can be found [here](Readme_CN.md).* You're a smart developer. You probably use [Alamofire](https://github.com/Alamofire/Alamofire) to abstract away access to `URLSession` and all those nasty details you don't really care about. But then, like lots of smart developers, you write ad hoc network abstraction layers. They are probably called "APIManager" or "NetworkModel", and they always end in tears. ![Moya Overview](web/diagram.png) Ad hoc network layers are common in iOS apps. They're bad for a few reasons: - Makes it hard to write new apps ("where do I begin?") - Makes it hard to maintain existing apps ("oh my god, this mess...") - Makes it hard to write unit tests ("how do I do this again?") So the basic idea of Moya is that we want some network abstraction layer that sufficiently encapsulates actually calling Alamofire directly. It should be simple enough that common things are easy, but comprehensive enough that complicated things are also easy. > If you use Alamofire to abstract away `URLSession`, why not use something to abstract away the nitty gritty of URLs, parameters, etc? Some awesome features of Moya: - Compile-time checking for correct API endpoint accesses. - Lets you define a clear usage of different endpoints with associated enum values. - Treats test stubs as first-class citizens so unit testing is super-easy. You can check out more about the project direction in the [vision document](Vision.md). ## Sample Project There's a sample project in the Demo directory. To use it, run `pod install` to download the required libraries. Have fun! ## Project Status This project is actively under development, and is being used in [Artsy's new auction app](https://github.com/Artsy/eidolon). We consider it ready for production use. ## Installation ### Moya version vs Swift version. Below is a table that shows which version of Moya you should use for your Swift version. | Swift | Moya | RxMoya | ReactiveMoya | | ----- | ------------- |---------------|---------------| | 4.X | >= 9.0 | >= 10.0 | >= 9.0 | | 3.X | 8.0.0 - 8.0.5 | 8.0.0 - 8.0.5 | 8.0.0 - 8.0.5 | | 2.3 | 7.0.2 - 7.0.4 | 7.0.2 - 7.0.4 | 7.0.2 - 7.0.4 | | 2.2 | <= 7.0.1 | <= 7.0.1 | <= 7.0.1 | **Upgrading to a new major version of Moya? Check out our [migration guides](https://github.com/Moya/Moya/blob/master/docs/MigrationGuides).** ### Swift Package Manager To integrate using Apple's Swift package manager, add the following as a dependency to your `Package.swift`: ```swift .package(url: "https://github.com/Moya/Moya.git", .upToNextMajor(from: "10.0.0")) ``` and then specify `"Moya"` as a dependency of the Target in which you wish to use Moya. If you want to use reactive extensions, add also `"ReactiveMoya"` or `"RxMoya"` as your Target dependency respectively. Here's an example `PackageDescription`: ```swift // swift-tools-version:4.0 import PackageDescription let package = Package( name: "MyPackage", products: [ .library( name: "MyPackage", targets: ["MyPackage"]), ], dependencies: [ .package(url: "https://github.com/Moya/Moya.git", .upToNextMajor(from: "10.0.0")) ], targets: [ .target( name: "MyPackage", dependencies: ["ReactiveMoya"]) ] ) ``` Note that as of Moya 10, SPM only works with Swift 4 toolchain and greater. ### CocoaPods For Moya, use the following entry in your Podfile: ```rb pod 'Moya' # or pod 'Moya/RxSwift' # or pod 'Moya/ReactiveSwift' ``` Then run `pod install`. In any file you'd like to use Moya in, don't forget to import the framework with `import Moya`. ### Carthage Carthage users can point to this repository and use whichever generated framework they'd like, `Moya`, `RxMoya`, or `ReactiveMoya`. Make the following entry in your Cartfile: ``` github "Moya/Moya" ``` Then run `carthage update`. If this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). > NOTE: At this time, Carthage does not provide a way to build only specific repository submodules. All submodules and their dependencies will be built with the above command. However, you don't need to copy frameworks you aren't using into your project. For instance, if you aren't using `ReactiveSwift`, feel free to delete that framework along with `ReactiveMoya` from the Carthage Build directory after `carthage update` completes. Or if you are using `ReactiveSwift` but not `RxSwift`, then `RxMoya`, `RxTest`, `RxCocoa`, etc. can safely be deleted. ### Manually - Open up Terminal, `cd` into your top-level project directory, and run the following command *if* your project is not initialized as a git repository: ```bash $ git init ``` - Add Alamofire, Result & Moya as a git [submodule](http://git-scm.com/docs/git-submodule) by running the following commands: ```bash $ git submodule add https://github.com/Alamofire/Alamofire.git $ git submodule add https://github.com/antitypical/Result.git $ git submodule add https://github.com/Moya/Moya.git ``` - Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project. Do the same with the `Result.xcodeproj` in the `Result` folder and `Moya.xcodeproj` in the `Moya` folder. > They should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter. - Verify that the deployment targets of the `xcodeproj`s match that of your application target in the Project Navigator. - Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar. - In the tab bar at the top of that window, open the "General" panel. - Click on the `+` button under the "Embedded Binaries" section. - You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder. > It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`. - Select the top `Alamofire.framework` for iOS and the bottom one for OS X. > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as either `Alamofire iOS`, `Alamofire macOS`, `Alamofire tvOS` or `Alamofire watchOS`. - Click on the `+` button under "Embedded Binaries" again and add the build target you need for `Result`. - Click on the `+` button again and add the correct build target for `Moya`. - And that's it! > The three frameworks are automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device. ## Usage After [some setup](docs/Examples/Basic.md), using Moya is really simple. You can access an API like this: ```swift provider = MoyaProvider() provider.request(.zen) { result in switch result { case let .success(moyaResponse): let data = moyaResponse.data let statusCode = moyaResponse.statusCode // do something with the response data or statusCode case let .failure(error): // this means there was a network failure - either the request // wasn't sent (connectivity), or no response was received (server // timed out). If the server responds with a 4xx or 5xx error, that // will be sent as a ".success"-ful response. } } ``` That's a basic example. Many API requests need parameters. Moya encodes these into the enum you use to access the endpoint, like this: ```swift provider = MoyaProvider() provider.request(.userProfile("ashfurrow")) { result in // do something with the result } ``` No more typos in URLs. No more missing parameter values. No more messing with parameter encoding. For more examples, see the [documentation](docs/Examples). ## Reactive Extensions Even cooler are the reactive extensions. Moya provides reactive extensions for [ReactiveSwift](https://github.com/ReactiveCocoa/ReactiveSwift) and [RxSwift](https://github.com/ReactiveX/RxSwift). ### ReactiveSwift [`ReactiveSwift` extension](docs/ReactiveSwift.md) provides both `reactive.request(:callbackQueue:)` and `reactive.requestWithProgress(:callbackQueue:)` methods that immediately return `SignalProducer`s that you can start, bind, map, or whatever you want to do. To handle errors, for instance, we could do the following: ```swift provider = MoyaProvider() provider.reactive.request(.userProfile("ashfurrow")).start { event in switch event { case let .value(response): image = UIImage(data: response.data) case let .failed(error): print(error) default: break } } ``` ### RxSwift [`RxSwift` extension](docs/RxSwift.md) also provide both `rx.request(:callbackQueue:)` and `rx.requestWithProgress(:callbackQueue:)` methods, but return type is different for both. In case of a normal `rx.request(:callbackQueue)`, the return type is `Single` which emits either single element or an error. In case of a `rx.requestWithProgress(:callbackQueue:)`, the return type is `Observable`, since we may get multiple events from progress and one last event which is a response. To handle errors, for instance, we could do the following: ```swift provider = MoyaProvider() provider.rx.request(.userProfile("ashfurrow")).subscribe { event in switch event { case let .success(response): image = UIImage(data: response.data) case let .error(error): print(error) } } ``` In addition to the option of using signals instead of callback blocks, there are also a series of signal operators for RxSwift and ReactiveSwift that will attempt to map the data received from the network response into either an image, some JSON, or a string, with `mapImage()`, `mapJSON()`, and `mapString()`, respectively. If the mapping is unsuccessful, you'll get an error on the signal. You also get handy methods for filtering out certain status codes. This means that you can place your code for handling API errors like 400's in the same places as code for handling invalid responses. ## Community Projects [Moya has a great community around it and some people have created some very helpful extensions](https://github.com/Moya/Moya/blob/master/docs/CommunityProjects.md). ## Contributing Hey! Do you like Moya? Awesome! We could actually really use your help! Open source isn't just writing code. Moya could use your help with any of the following: - Finding (and reporting!) bugs. - New feature suggestions. - Answering questions on issues. - Documentation improvements. - Reviewing pull requests. - Helping to manage issue priorities. - Fixing bugs/new features. If any of that sounds cool to you, send a pull request! After your first contribution, we will add you as a member to the repo so you can merge pull requests and help steer the ship :ship: You can read more details about that [in our contributor guidelines](https://github.com/Moya/Moya/blob/master/Contributing.md). Moya's community has a tremendous positive energy, and the maintainers are committed to keeping things awesome. Like [in the CocoaPods community](https://github.com/CocoaPods/CocoaPods/wiki/Communication-&-Design-Rules), always assume positive intent; even if a comment sounds mean-spirited, give the person the benefit of the doubt. Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by [its terms](https://github.com/Moya/Moya/blob/master/Code%20of%20Conduct.md). ### Adding new source files If you add or remove a source file from Moya, a corresponding change needs to be made to the `Moya.xcodeproj` project at the root of this repository. This project is used for Carthage. Don't worry, you'll get an automated warning when submitting a pull request if you forget. ### Help us improve Moya documentation Whether you’re a core member or a user trying it out for the first time, you can make a valuable contribution to Moya by improving the documentation. Help us by: - sending us feedback about something you thought was confusing or simply missing - suggesting better wording or ways of explaining certain topics - sending us a pull request via GitHub - improving the [Chinese documentation](Readme_CN.md) ## License Moya is released under an MIT license. See [License.md](License.md) for more information. ================================================ FILE: Pods/Moya/Sources/Moya/AnyEncodable.swift ================================================ import Foundation struct AnyEncodable: Encodable { private let encodable: Encodable public init(_ encodable: Encodable) { self.encodable = encodable } func encode(to encoder: Encoder) throws { try encodable.encode(to: encoder) } } ================================================ FILE: Pods/Moya/Sources/Moya/Cancellable.swift ================================================ /// Protocol to define the opaque type returned from a request public protocol Cancellable { var isCancelled: Bool { get } func cancel() } internal class CancellableWrapper: Cancellable { internal var innerCancellable: Cancellable = SimpleCancellable() var isCancelled: Bool { return innerCancellable.isCancelled } internal func cancel() { innerCancellable.cancel() } } internal class SimpleCancellable: Cancellable { var isCancelled = false func cancel() { isCancelled = true } } ================================================ FILE: Pods/Moya/Sources/Moya/Endpoint.swift ================================================ import Foundation /// Used for stubbing responses. public enum EndpointSampleResponse { /// The network returned a response, including status code and data. case networkResponse(Int, Data) /// The network returned response which can be fully customized. case response(HTTPURLResponse, Data) /// The network failed to send the request, or failed to retrieve a response (eg a timeout). case networkError(NSError) } /// Class for reifying a target of the `Target` enum unto a concrete `Endpoint`. open class Endpoint { public typealias SampleResponseClosure = () -> EndpointSampleResponse open let url: String open let sampleResponseClosure: SampleResponseClosure open let method: Moya.Method open let task: Task open let httpHeaderFields: [String: String]? /// Main initializer for `Endpoint`. public init(url: String, sampleResponseClosure: @escaping SampleResponseClosure, method: Moya.Method, task: Task, httpHeaderFields: [String: String]?) { self.url = url self.sampleResponseClosure = sampleResponseClosure self.method = method self.task = task self.httpHeaderFields = httpHeaderFields } /// Convenience method for creating a new `Endpoint` with the same properties as the receiver, but with added HTTP header fields. open func adding(newHTTPHeaderFields: [String: String]) -> Endpoint { return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: add(httpHeaderFields: newHTTPHeaderFields)) } /// Convenience method for creating a new `Endpoint` with the same properties as the receiver, but with replaced `task` parameter. open func replacing(task: Task) -> Endpoint { return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: httpHeaderFields) } fileprivate func add(httpHeaderFields headers: [String: String]?) -> [String: String]? { guard let unwrappedHeaders = headers, unwrappedHeaders.isEmpty == false else { return self.httpHeaderFields } var newHTTPHeaderFields = self.httpHeaderFields ?? [:] unwrappedHeaders.forEach { key, value in newHTTPHeaderFields[key] = value } return newHTTPHeaderFields } } /// Extension for converting an `Endpoint` into a `URLRequest`. extension Endpoint { /// Returns the `Endpoint` converted to a `URLRequest` if valid. Throws an error otherwise. public func urlRequest() throws -> URLRequest { guard let requestURL = Foundation.URL(string: url) else { throw MoyaError.requestMapping(url) } var request = URLRequest(url: requestURL) request.httpMethod = method.rawValue request.allHTTPHeaderFields = httpHeaderFields switch task { case .requestPlain, .uploadFile, .uploadMultipart, .downloadDestination: return request case .requestData(let data): request.httpBody = data return request case let .requestJSONEncodable(encodable): return try request.encoded(encodable: encodable) case let .requestParameters(parameters, parameterEncoding): return try request.encoded(parameters: parameters, parameterEncoding: parameterEncoding) case let .uploadCompositeMultipart(_, urlParameters): let parameterEncoding = URLEncoding(destination: .queryString) return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding) case let .downloadParameters(parameters, parameterEncoding, _): return try request.encoded(parameters: parameters, parameterEncoding: parameterEncoding) case let .requestCompositeData(bodyData: bodyData, urlParameters: urlParameters): request.httpBody = bodyData let parameterEncoding = URLEncoding(destination: .queryString) return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding) case let .requestCompositeParameters(bodyParameters: bodyParameters, bodyEncoding: bodyParameterEncoding, urlParameters: urlParameters): if bodyParameterEncoding is URLEncoding { fatalError("URLEncoding is disallowed as bodyEncoding.") } let bodyfulRequest = try request.encoded(parameters: bodyParameters, parameterEncoding: bodyParameterEncoding) let urlEncoding = URLEncoding(destination: .queryString) return try bodyfulRequest.encoded(parameters: urlParameters, parameterEncoding: urlEncoding) } } } /// Required for using `Endpoint` as a key type in a `Dictionary`. extension Endpoint: Equatable, Hashable { public var hashValue: Int { let request = try? urlRequest() return request?.hashValue ?? url.hashValue } /// Note: If both Endpoints fail to produce a URLRequest the comparison will /// fall back to comparing each Endpoint's hashValue. public static func == (lhs: Endpoint, rhs: Endpoint) -> Bool { let lhsRequest = try? lhs.urlRequest() let rhsRequest = try? rhs.urlRequest() if lhsRequest != nil, rhsRequest == nil { return false } if lhsRequest == nil, rhsRequest != nil { return false } if lhsRequest == nil, rhsRequest == nil { return lhs.hashValue == rhs.hashValue } return (lhsRequest == rhsRequest) } } ================================================ FILE: Pods/Moya/Sources/Moya/Image.swift ================================================ #if os(iOS) || os(watchOS) || os(tvOS) import UIKit.UIImage public typealias ImageType = UIImage #elseif os(OSX) import AppKit.NSImage public typealias ImageType = NSImage #endif /// An alias for the SDK's image type. public typealias Image = ImageType ================================================ FILE: Pods/Moya/Sources/Moya/Moya+Alamofire.swift ================================================ import Foundation import Alamofire public typealias Manager = Alamofire.SessionManager internal typealias Request = Alamofire.Request internal typealias DownloadRequest = Alamofire.DownloadRequest internal typealias UploadRequest = Alamofire.UploadRequest internal typealias DataRequest = Alamofire.DataRequest internal typealias URLRequestConvertible = Alamofire.URLRequestConvertible /// Represents an HTTP method. public typealias Method = Alamofire.HTTPMethod /// Choice of parameter encoding. public typealias ParameterEncoding = Alamofire.ParameterEncoding public typealias JSONEncoding = Alamofire.JSONEncoding public typealias URLEncoding = Alamofire.URLEncoding public typealias PropertyListEncoding = Alamofire.PropertyListEncoding /// Multipart form public typealias RequestMultipartFormData = Alamofire.MultipartFormData /// Multipart form data encoding result. public typealias MultipartFormDataEncodingResult = Manager.MultipartFormDataEncodingResult public typealias DownloadDestination = Alamofire.DownloadRequest.DownloadFileDestination /// Make the Alamofire Request type conform to our type, to prevent leaking Alamofire to plugins. extension Request: RequestType { } /// Internal token that can be used to cancel requests public final class CancellableToken: Cancellable, CustomDebugStringConvertible { let cancelAction: () -> Void let request: Request? public fileprivate(set) var isCancelled = false fileprivate var lock: DispatchSemaphore = DispatchSemaphore(value: 1) public func cancel() { _ = lock.wait(timeout: DispatchTime.distantFuture) defer { lock.signal() } guard !isCancelled else { return } isCancelled = true cancelAction() } public init(action: @escaping () -> Void) { self.cancelAction = action self.request = nil } init(request: Request) { self.request = request self.cancelAction = { request.cancel() } } public var debugDescription: String { guard let request = self.request else { return "Empty Request" } return request.debugDescription } } internal typealias RequestableCompletion = (HTTPURLResponse?, URLRequest?, Data?, Swift.Error?) -> Void internal protocol Requestable { func response(callbackQueue: DispatchQueue?, completionHandler: @escaping RequestableCompletion) -> Self } extension DataRequest: Requestable { internal func response(callbackQueue: DispatchQueue?, completionHandler: @escaping RequestableCompletion) -> Self { return response(queue: callbackQueue) { handler in completionHandler(handler.response, handler.request, handler.data, handler.error) } } } extension DownloadRequest: Requestable { internal func response(callbackQueue: DispatchQueue?, completionHandler: @escaping RequestableCompletion) -> Self { return response(queue: callbackQueue) { handler in completionHandler(handler.response, handler.request, nil, handler.error) } } } ================================================ FILE: Pods/Moya/Sources/Moya/MoyaError.swift ================================================ import Foundation public enum MoyaError: Swift.Error { /// Indicates a response failed to map to an image. case imageMapping(Response) /// Indicates a response failed to map to a JSON structure. case jsonMapping(Response) /// Indicates a response failed to map to a String. case stringMapping(Response) /// Indicates a response failed to map to a Decodable object. case objectMapping(Swift.Error, Response) /// Indicates that Encodable couldn't be encoded into Data case encodableMapping(Swift.Error) /// Indicates a response failed with an invalid HTTP status code. case statusCode(Response) /// Indicates a response failed due to an underlying `Error`. case underlying(Swift.Error, Response?) /// Indicates that an `Endpoint` failed to map to a `URLRequest`. case requestMapping(String) /// Indicates that an `Endpoint` failed to encode the parameters for the `URLRequest`. case parameterEncoding(Swift.Error) } public extension MoyaError { /// Depending on error type, returns a `Response` object. var response: Moya.Response? { switch self { case .imageMapping(let response): return response case .jsonMapping(let response): return response case .stringMapping(let response): return response case .objectMapping(_, let response): return response case .statusCode(let response): return response case .underlying(_, let response): return response case .encodableMapping: return nil case .requestMapping: return nil case .parameterEncoding: return nil } } } // MARK: - Error Descriptions extension MoyaError: LocalizedError { public var errorDescription: String? { switch self { case .imageMapping: return "Failed to map data to an Image." case .jsonMapping: return "Failed to map data to JSON." case .stringMapping: return "Failed to map data to a String." case .objectMapping: return "Failed to map data to a Decodable object." case .encodableMapping: return "Failed to encode Encodable object into data." case .statusCode: return "Status code didn't fall within the given range." case .requestMapping: return "Failed to map Endpoint to a URLRequest." case .parameterEncoding(let error): return "Failed to encode parameters for URLRequest. \(error.localizedDescription)" case .underlying(let error, _): return error.localizedDescription } } } ================================================ FILE: Pods/Moya/Sources/Moya/MoyaProvider+Defaults.swift ================================================ import Foundation /// These functions are default mappings to `MoyaProvider`'s properties: endpoints, requests, manager, etc. public extension MoyaProvider { public final class func defaultEndpointMapping(for target: Target) -> Endpoint { return Endpoint( url: URL(target: target).absoluteString, sampleResponseClosure: { .networkResponse(200, target.sampleData) }, method: target.method, task: target.task, httpHeaderFields: target.headers ) } public final class func defaultRequestMapping(for endpoint: Endpoint, closure: RequestResultClosure) { do { let urlRequest = try endpoint.urlRequest() closure(.success(urlRequest)) } catch MoyaError.requestMapping(let url) { closure(.failure(MoyaError.requestMapping(url))) } catch MoyaError.parameterEncoding(let error) { closure(.failure(MoyaError.parameterEncoding(error))) } catch { closure(.failure(MoyaError.underlying(error, nil))) } } public final class func defaultAlamofireManager() -> Manager { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = Manager.defaultHTTPHeaders let manager = Manager(configuration: configuration) manager.startRequestsImmediately = false return manager } } ================================================ FILE: Pods/Moya/Sources/Moya/MoyaProvider+Internal.swift ================================================ import Foundation import Result // MARK: - Method extension Method { public var supportsMultipart: Bool { switch self { case .post, .put, .patch, .connect: return true case .get, .delete, .head, .options, .trace: return false } } } // MARK: - MoyaProvider /// Internal extension to keep the inner-workings outside the main Moya.swift file. public extension MoyaProvider { /// Performs normal requests. func requestNormal(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> Cancellable { let endpoint = self.endpoint(target) let stubBehavior = self.stubClosure(target) let cancellableToken = CancellableWrapper() // Allow plugins to modify response let pluginsWithCompletion: Moya.Completion = { result in let processedResult = self.plugins.reduce(result) { $1.process($0, target: target) } completion(processedResult) } if trackInflights { objc_sync_enter(self) var inflightCompletionBlocks = self.inflightRequests[endpoint] inflightCompletionBlocks?.append(pluginsWithCompletion) self.inflightRequests[endpoint] = inflightCompletionBlocks objc_sync_exit(self) if inflightCompletionBlocks != nil { return cancellableToken } else { objc_sync_enter(self) self.inflightRequests[endpoint] = [pluginsWithCompletion] objc_sync_exit(self) } } let performNetworking = { (requestResult: Result) in if cancellableToken.isCancelled { self.cancelCompletion(pluginsWithCompletion, target: target) return } var request: URLRequest! switch requestResult { case .success(let urlRequest): request = urlRequest case .failure(let error): pluginsWithCompletion(.failure(error)) return } // Allow plugins to modify request let preparedRequest = self.plugins.reduce(request) { $1.prepare($0, target: target) } let networkCompletion: Moya.Completion = { result in if self.trackInflights { self.inflightRequests[endpoint]?.forEach { $0(result) } objc_sync_enter(self) self.inflightRequests.removeValue(forKey: endpoint) objc_sync_exit(self) } else { pluginsWithCompletion(result) } } cancellableToken.innerCancellable = self.performRequest(target, request: preparedRequest, callbackQueue: callbackQueue, progress: progress, completion: networkCompletion, endpoint: endpoint, stubBehavior: stubBehavior) } requestClosure(endpoint, performNetworking) return cancellableToken } // swiftlint:disable:next function_parameter_count private func performRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion, endpoint: Endpoint, stubBehavior: Moya.StubBehavior) -> Cancellable { switch stubBehavior { case .never: switch target.task { case .requestPlain, .requestData, .requestJSONEncodable, .requestParameters, .requestCompositeData, .requestCompositeParameters: return self.sendRequest(target, request: request, callbackQueue: callbackQueue, progress: progress, completion: completion) case .uploadFile(let file): return self.sendUploadFile(target, request: request, callbackQueue: callbackQueue, file: file, progress: progress, completion: completion) case .uploadMultipart(let multipartBody), .uploadCompositeMultipart(let multipartBody, _): guard !multipartBody.isEmpty && target.method.supportsMultipart else { fatalError("\(target) is not a multipart upload target.") } return self.sendUploadMultipart(target, request: request, callbackQueue: callbackQueue, multipartBody: multipartBody, progress: progress, completion: completion) case .downloadDestination(let destination), .downloadParameters(_, _, let destination): return self.sendDownloadRequest(target, request: request, callbackQueue: callbackQueue, destination: destination, progress: progress, completion: completion) } default: return self.stubRequest(target, request: request, callbackQueue: callbackQueue, completion: completion, endpoint: endpoint, stubBehavior: stubBehavior) } } func cancelCompletion(_ completion: Moya.Completion, target: Target) { let error = MoyaError.underlying(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled, userInfo: nil), nil) plugins.forEach { $0.didReceive(.failure(error), target: target) } completion(.failure(error)) } /// Creates a function which, when called, executes the appropriate stubbing behavior for the given parameters. public final func createStubFunction(_ token: CancellableToken, forTarget target: Target, withCompletion completion: @escaping Moya.Completion, endpoint: Endpoint, plugins: [PluginType], request: URLRequest) -> (() -> Void) { // swiftlint:disable:this function_parameter_count return { if token.isCancelled { self.cancelCompletion(completion, target: target) return } switch endpoint.sampleResponseClosure() { case .networkResponse(let statusCode, let data): let response = Moya.Response(statusCode: statusCode, data: data, request: request, response: nil) plugins.forEach { $0.didReceive(.success(response), target: target) } completion(.success(response)) case .response(let customResponse, let data): let response = Moya.Response(statusCode: customResponse.statusCode, data: data, request: request, response: customResponse) plugins.forEach { $0.didReceive(.success(response), target: target) } completion(.success(response)) case .networkError(let error): let error = MoyaError.underlying(error, nil) plugins.forEach { $0.didReceive(.failure(error), target: target) } completion(.failure(error)) } } } /// Notify all plugins that a stub is about to be performed. You must call this if overriding `stubRequest`. final func notifyPluginsOfImpendingStub(for request: URLRequest, target: Target) { let alamoRequest = manager.request(request as URLRequestConvertible) plugins.forEach { $0.willSend(alamoRequest, target: target) } alamoRequest.cancel() } } private extension MoyaProvider { func sendUploadMultipart(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, multipartBody: [MultipartFormData], progress: Moya.ProgressBlock? = nil, completion: @escaping Moya.Completion) -> CancellableWrapper { let cancellable = CancellableWrapper() let multipartFormData: (RequestMultipartFormData) -> Void = { form in for bodyPart in multipartBody { switch bodyPart.provider { case .data(let data): form.append(data: data, bodyPart: bodyPart) case .file(let url): form.append(fileURL: url, bodyPart: bodyPart) case .stream(let stream, let length): form.append(stream: stream, length: length, bodyPart: bodyPart) } } } manager.upload(multipartFormData: multipartFormData, with: request) { result in switch result { case .success(let alamoRequest, _, _): if cancellable.isCancelled { self.cancelCompletion(completion, target: target) return } cancellable.innerCancellable = self.sendAlamofireRequest(alamoRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) case .failure(let error): completion(.failure(MoyaError.underlying(error as NSError, nil))) } } return cancellable } func sendUploadFile(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, file: URL, progress: ProgressBlock? = nil, completion: @escaping Completion) -> CancellableToken { let uploadRequest = manager.upload(file, with: request) let alamoRequest = target.validate ? uploadRequest.validate() : uploadRequest return self.sendAlamofireRequest(alamoRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) } func sendDownloadRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, destination: @escaping DownloadDestination, progress: ProgressBlock? = nil, completion: @escaping Completion) -> CancellableToken { let downloadRequest = manager.download(request, to: destination) let alamoRequest = target.validate ? downloadRequest.validate() : downloadRequest return self.sendAlamofireRequest(alamoRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) } func sendRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> CancellableToken { let initialRequest = manager.request(request as URLRequestConvertible) let alamoRequest = target.validate ? initialRequest.validate() : initialRequest return sendAlamofireRequest(alamoRequest, target: target, callbackQueue: callbackQueue, progress: progress, completion: completion) } // swiftlint:disable:next cyclomatic_complexity func sendAlamofireRequest(_ alamoRequest: T, target: Target, callbackQueue: DispatchQueue?, progress progressCompletion: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> CancellableToken where T: Requestable, T: Request { // Give plugins the chance to alter the outgoing request let plugins = self.plugins plugins.forEach { $0.willSend(alamoRequest, target: target) } var progressAlamoRequest = alamoRequest let progressClosure: (Progress) -> Void = { progress in let sendProgress: () -> Void = { progressCompletion?(ProgressResponse(progress: progress)) } if let callbackQueue = callbackQueue { callbackQueue.async(execute: sendProgress) } else { sendProgress() } } // Perform the actual request if progressCompletion != nil { switch progressAlamoRequest { case let downloadRequest as DownloadRequest: if let downloadRequest = downloadRequest.downloadProgress(closure: progressClosure) as? T { progressAlamoRequest = downloadRequest } case let uploadRequest as UploadRequest: if let uploadRequest = uploadRequest.uploadProgress(closure: progressClosure) as? T { progressAlamoRequest = uploadRequest } case let dataRequest as DataRequest: if let dataRequest = dataRequest.downloadProgress(closure: progressClosure) as? T { progressAlamoRequest = dataRequest } default: break } } let completionHandler: RequestableCompletion = { response, request, data, error in let result = convertResponseToResult(response, request: request, data: data, error: error) // Inform all plugins about the response plugins.forEach { $0.didReceive(result, target: target) } if let progressCompletion = progressCompletion { switch progressAlamoRequest { case let downloadRequest as DownloadRequest: progressCompletion(ProgressResponse(progress: downloadRequest.progress, response: result.value)) case let uploadRequest as UploadRequest: progressCompletion(ProgressResponse(progress: uploadRequest.uploadProgress, response: result.value)) case let dataRequest as DataRequest: progressCompletion(ProgressResponse(progress: dataRequest.progress, response: result.value)) default: progressCompletion(ProgressResponse(response: result.value)) } } completion(result) } progressAlamoRequest = progressAlamoRequest.response(callbackQueue: callbackQueue, completionHandler: completionHandler) progressAlamoRequest.resume() return CancellableToken(request: progressAlamoRequest) } } ================================================ FILE: Pods/Moya/Sources/Moya/MoyaProvider.swift ================================================ import Foundation import Result /// Closure to be executed when a request has completed. public typealias Completion = (_ result: Result) -> Void /// Closure to be executed when progress changes. public typealias ProgressBlock = (_ progress: ProgressResponse) -> Void public struct ProgressResponse { public let response: Response? public let progressObject: Progress? public init(progress: Progress? = nil, response: Response? = nil) { self.progressObject = progress self.response = response } public var progress: Double { return progressObject?.fractionCompleted ?? 1.0 } public var completed: Bool { return progress == 1.0 && response != nil } } public protocol MoyaProviderType: class { associatedtype Target: TargetType func request(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> Cancellable } /// Request provider class. Requests should be made through this class only. open class MoyaProvider: MoyaProviderType { /// Closure that defines the endpoints for the provider. public typealias EndpointClosure = (Target) -> Endpoint /// Closure that decides if and what request should be performed public typealias RequestResultClosure = (Result) -> Void /// Closure that resolves an `Endpoint` into a `RequestResult`. public typealias RequestClosure = (Endpoint, @escaping RequestResultClosure) -> Void /// Closure that decides if/how a request should be stubbed. public typealias StubClosure = (Target) -> Moya.StubBehavior open let endpointClosure: EndpointClosure open let requestClosure: RequestClosure open let stubClosure: StubClosure open let manager: Manager /// A list of plugins /// e.g. for logging, network activity indicator or credentials open let plugins: [PluginType] open let trackInflights: Bool open internal(set) var inflightRequests: [Endpoint: [Moya.Completion]] = [:] /// Propagated to Alamofire as callback queue. If nil - the Alamofire default (as of their API in 2017 - the main queue) will be used. let callbackQueue: DispatchQueue? /// Initializes a provider. public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping, requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping, stubClosure: @escaping StubClosure = MoyaProvider.neverStub, callbackQueue: DispatchQueue? = nil, manager: Manager = MoyaProvider.defaultAlamofireManager(), plugins: [PluginType] = [], trackInflights: Bool = false) { self.endpointClosure = endpointClosure self.requestClosure = requestClosure self.stubClosure = stubClosure self.manager = manager self.plugins = plugins self.trackInflights = trackInflights self.callbackQueue = callbackQueue } /// Returns an `Endpoint` based on the token, method, and parameters by invoking the `endpointClosure`. open func endpoint(_ token: Target) -> Endpoint { return endpointClosure(token) } /// Designated request-making method. Returns a `Cancellable` token to cancel the request later. @discardableResult open func request(_ target: Target, callbackQueue: DispatchQueue? = .none, progress: ProgressBlock? = .none, completion: @escaping Completion) -> Cancellable { let callbackQueue = callbackQueue ?? self.callbackQueue return requestNormal(target, callbackQueue: callbackQueue, progress: progress, completion: completion) } // swiftlint:disable function_parameter_count /// When overriding this method, take care to `notifyPluginsOfImpendingStub` and to perform the stub using the `createStubFunction` method. /// Note: this was previously in an extension, however it must be in the original class declaration to allow subclasses to override. @discardableResult open func stubRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, completion: @escaping Moya.Completion, endpoint: Endpoint, stubBehavior: Moya.StubBehavior) -> CancellableToken { let callbackQueue = callbackQueue ?? self.callbackQueue let cancellableToken = CancellableToken { } notifyPluginsOfImpendingStub(for: request, target: target) let plugins = self.plugins let stub: () -> Void = createStubFunction(cancellableToken, forTarget: target, withCompletion: completion, endpoint: endpoint, plugins: plugins, request: request) switch stubBehavior { case .immediate: switch callbackQueue { case .none: stub() case .some(let callbackQueue): callbackQueue.async(execute: stub) } case .delayed(let delay): let killTimeOffset = Int64(CDouble(delay) * CDouble(NSEC_PER_SEC)) let killTime = DispatchTime.now() + Double(killTimeOffset) / Double(NSEC_PER_SEC) (callbackQueue ?? DispatchQueue.main).asyncAfter(deadline: killTime) { stub() } case .never: fatalError("Method called to stub request when stubbing is disabled.") } return cancellableToken } // swiftlint:enable function_parameter_count } /// Mark: Stubbing /// Controls how stub responses are returned. public enum StubBehavior { /// Do not stub. case never /// Return a response immediately. case immediate /// Return a response after a delay. case delayed(seconds: TimeInterval) } public extension MoyaProvider { // Swift won't let us put the StubBehavior enum inside the provider class, so we'll // at least add some class functions to allow easy access to common stubbing closures. public final class func neverStub(_: Target) -> Moya.StubBehavior { return .never } public final class func immediatelyStub(_: Target) -> Moya.StubBehavior { return .immediate } public final class func delayedStub(_ seconds: TimeInterval) -> (Target) -> Moya.StubBehavior { return { _ in return .delayed(seconds: seconds) } } } public func convertResponseToResult(_ response: HTTPURLResponse?, request: URLRequest?, data: Data?, error: Swift.Error?) -> Result { switch (response, data, error) { case let (.some(response), data, .none): let response = Moya.Response(statusCode: response.statusCode, data: data ?? Data(), request: request, response: response) return .success(response) case let (.some(response), _, .some(error)): let response = Moya.Response(statusCode: response.statusCode, data: data ?? Data(), request: request, response: response) let error = MoyaError.underlying(error, response) return .failure(error) case let (_, _, .some(error)): let error = MoyaError.underlying(error, nil) return .failure(error) default: let error = MoyaError.underlying(NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil), nil) return .failure(error) } } ================================================ FILE: Pods/Moya/Sources/Moya/MultiTarget.swift ================================================ import Foundation /// A `TargetType` used to enable `MoyaProvider` to process multiple `TargetType`s. public enum MultiTarget: TargetType { /// The embedded `TargetType`. case target(TargetType) public init(_ target: TargetType) { self = MultiTarget.target(target) } public var path: String { return target.path } public var baseURL: URL { return target.baseURL } public var method: Moya.Method { return target.method } public var sampleData: Data { return target.sampleData } public var task: Task { return target.task } public var validate: Bool { return target.validate } public var headers: [String: String]? { return target.headers } /// The embedded `TargetType`. public var target: TargetType { switch self { case .target(let t): return t } } } ================================================ FILE: Pods/Moya/Sources/Moya/MultipartFormData.swift ================================================ import Foundation /// Represents "multipart/form-data" for an upload. public struct MultipartFormData { /// Method to provide the form data. public enum FormDataProvider { case data(Foundation.Data) case file(URL) case stream(InputStream, UInt64) } /// Initialize a new `MultipartFormData`. public init(provider: FormDataProvider, name: String, fileName: String? = nil, mimeType: String? = nil) { self.provider = provider self.name = name self.fileName = fileName self.mimeType = mimeType } /// The method being used for providing form data. public let provider: FormDataProvider /// The name. public let name: String /// The file name. public let fileName: String? /// The MIME type public let mimeType: String? } // MARK: RequestMultipartFormData appending internal extension RequestMultipartFormData { func append(data: Data, bodyPart: MultipartFormData) { if let mimeType = bodyPart.mimeType { if let fileName = bodyPart.fileName { append(data, withName: bodyPart.name, fileName: fileName, mimeType: mimeType) } else { append(data, withName: bodyPart.name, mimeType: mimeType) } } else { append(data, withName: bodyPart.name) } } func append(fileURL url: URL, bodyPart: MultipartFormData) { if let fileName = bodyPart.fileName, let mimeType = bodyPart.mimeType { append(url, withName: bodyPart.name, fileName: fileName, mimeType: mimeType) } else { append(url, withName: bodyPart.name) } } func append(stream: InputStream, length: UInt64, bodyPart: MultipartFormData) { append(stream, withLength: length, name: bodyPart.name, fileName: bodyPart.fileName ?? "", mimeType: bodyPart.mimeType ?? "") } } ================================================ FILE: Pods/Moya/Sources/Moya/Plugin.swift ================================================ import Foundation import Result /// A Moya Plugin receives callbacks to perform side effects wherever a request is sent or received. /// /// for example, a plugin may be used to /// - log network requests /// - hide and show a network activity indicator /// - inject additional information into a request public protocol PluginType { /// Called to modify a request before sending func prepare(_ request: URLRequest, target: TargetType) -> URLRequest /// Called immediately before a request is sent over the network (or stubbed). func willSend(_ request: RequestType, target: TargetType) /// Called after a response has been received, but before the MoyaProvider has invoked its completion handler. func didReceive(_ result: Result, target: TargetType) /// Called to modify a result before completion func process(_ result: Result, target: TargetType) -> Result } public extension PluginType { func prepare(_ request: URLRequest, target: TargetType) -> URLRequest { return request } func willSend(_ request: RequestType, target: TargetType) { } func didReceive(_ result: Result, target: TargetType) { } func process(_ result: Result, target: TargetType) -> Result { return result } } /// Request type used by `willSend` plugin function. public protocol RequestType { // Note: // // We use this protocol instead of the Alamofire request to avoid leaking that abstraction. // A plugin should not know about Alamofire at all. /// Retrieve an `NSURLRequest` representation. var request: URLRequest? { get } /// Authenticates the request with a username and password. func authenticate(user: String, password: String, persistence: URLCredential.Persistence) -> Self /// Authenticates the request with an `NSURLCredential` instance. func authenticate(usingCredential credential: URLCredential) -> Self } ================================================ FILE: Pods/Moya/Sources/Moya/Plugins/AccessTokenPlugin.swift ================================================ import Foundation import Result // MARK: - AccessTokenAuthorizable /// A protocol for controlling the behavior of `AccessTokenPlugin`. public protocol AccessTokenAuthorizable { /// Represents the authorization header to use for requests. var authorizationType: AuthorizationType { get } } // MARK: - AuthorizationType /// An enum representing the header to use with an `AccessTokenPlugin` public enum AuthorizationType: String { case none case basic = "Basic" case bearer = "Bearer" } // MARK: - AccessTokenPlugin /** A plugin for adding basic or bearer-type authorization headers to requests. Example: ``` Authorization: Bearer Authorization: Basic ``` */ public struct AccessTokenPlugin: PluginType { /// A closure returning the access token to be applied in the header. public let tokenClosure: () -> String /** Initialize a new `AccessTokenPlugin`. - parameters: - tokenClosure: A closure returning the token to be applied in the pattern `Authorization: ` */ public init(tokenClosure: @escaping @autoclosure () -> String) { self.tokenClosure = tokenClosure } /** Prepare a request by adding an authorization header if necessary. - parameters: - request: The request to modify. - target: The target of the request. - returns: The modified `URLRequest`. */ public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest { guard let authorizable = target as? AccessTokenAuthorizable else { return request } let authorizationType = authorizable.authorizationType var request = request switch authorizationType { case .basic, .bearer: let authValue = authorizationType.rawValue + " " + tokenClosure() request.addValue(authValue, forHTTPHeaderField: "Authorization") case .none: break } return request } } ================================================ FILE: Pods/Moya/Sources/Moya/Plugins/CredentialsPlugin.swift ================================================ import Foundation import Result /// Provides each request with optional URLCredentials. public final class CredentialsPlugin: PluginType { public typealias CredentialClosure = (TargetType) -> URLCredential? let credentialsClosure: CredentialClosure public init(credentialsClosure: @escaping CredentialClosure) { self.credentialsClosure = credentialsClosure } // MARK: Plugin public func willSend(_ request: RequestType, target: TargetType) { if let credentials = credentialsClosure(target) { _ = request.authenticate(usingCredential: credentials) } } } ================================================ FILE: Pods/Moya/Sources/Moya/Plugins/NetworkActivityPlugin.swift ================================================ import Foundation import Result /// Network activity change notification type. public enum NetworkActivityChangeType { case began, ended } /// Notify a request's network activity changes (request begins or ends). public final class NetworkActivityPlugin: PluginType { public typealias NetworkActivityClosure = (_ change: NetworkActivityChangeType, _ target: TargetType) -> Void let networkActivityClosure: NetworkActivityClosure public init(networkActivityClosure: @escaping NetworkActivityClosure) { self.networkActivityClosure = networkActivityClosure } // MARK: Plugin /// Called by the provider as soon as the request is about to start public func willSend(_ request: RequestType, target: TargetType) { networkActivityClosure(.began, target) } /// Called by the provider as soon as a response arrives, even if the request is canceled. public func didReceive(_ result: Result, target: TargetType) { networkActivityClosure(.ended, target) } } ================================================ FILE: Pods/Moya/Sources/Moya/Plugins/NetworkLoggerPlugin.swift ================================================ import Foundation import Result /// Logs network activity (outgoing requests and incoming responses). public final class NetworkLoggerPlugin: PluginType { fileprivate let loggerId = "Moya_Logger" fileprivate let dateFormatString = "dd/MM/yyyy HH:mm:ss" fileprivate let dateFormatter = DateFormatter() fileprivate let separator = ", " fileprivate let terminator = "\n" fileprivate let cURLTerminator = "\\\n" fileprivate let output: (_ separator: String, _ terminator: String, _ items: Any...) -> Void fileprivate let requestDataFormatter: ((Data) -> (String))? fileprivate let responseDataFormatter: ((Data) -> (Data))? /// If true, also logs response body data. public let isVerbose: Bool public let cURL: Bool public init(verbose: Bool = false, cURL: Bool = false, output: ((_ separator: String, _ terminator: String, _ items: Any...) -> Void)? = nil, requestDataFormatter: ((Data) -> (String))? = nil, responseDataFormatter: ((Data) -> (Data))? = nil) { self.cURL = cURL self.isVerbose = verbose self.output = output ?? NetworkLoggerPlugin.reversedPrint self.requestDataFormatter = requestDataFormatter self.responseDataFormatter = responseDataFormatter } public func willSend(_ request: RequestType, target: TargetType) { if let request = request as? CustomDebugStringConvertible, cURL { output(separator, terminator, request.debugDescription) return } outputItems(logNetworkRequest(request.request as URLRequest?)) } public func didReceive(_ result: Result, target: TargetType) { if case .success(let response) = result { outputItems(logNetworkResponse(response.response, data: response.data, target: target)) } else { outputItems(logNetworkResponse(nil, data: nil, target: target)) } } fileprivate func outputItems(_ items: [String]) { if isVerbose { items.forEach { output(separator, terminator, $0) } } else { output(separator, terminator, items) } } } private extension NetworkLoggerPlugin { var date: String { dateFormatter.dateFormat = dateFormatString dateFormatter.locale = Locale(identifier: "en_US_POSIX") return dateFormatter.string(from: Date()) } func format(_ loggerId: String, date: String, identifier: String, message: String) -> String { return "\(loggerId): [\(date)] \(identifier): \(message)" } func logNetworkRequest(_ request: URLRequest?) -> [String] { var output = [String]() output += [format(loggerId, date: date, identifier: "Request", message: request?.description ?? "(invalid request)")] if let headers = request?.allHTTPHeaderFields { output += [format(loggerId, date: date, identifier: "Request Headers", message: headers.description)] } if let bodyStream = request?.httpBodyStream { output += [format(loggerId, date: date, identifier: "Request Body Stream", message: bodyStream.description)] } if let httpMethod = request?.httpMethod { output += [format(loggerId, date: date, identifier: "HTTP Request Method", message: httpMethod)] } if let body = request?.httpBody, let stringOutput = requestDataFormatter?(body) ?? String(data: body, encoding: .utf8), isVerbose { output += [format(loggerId, date: date, identifier: "Request Body", message: stringOutput)] } return output } func logNetworkResponse(_ response: HTTPURLResponse?, data: Data?, target: TargetType) -> [String] { guard let response = response else { return [format(loggerId, date: date, identifier: "Response", message: "Received empty network response for \(target).")] } var output = [String]() output += [format(loggerId, date: date, identifier: "Response", message: response.description)] if let data = data, let stringData = String(data: responseDataFormatter?(data) ?? data, encoding: String.Encoding.utf8), isVerbose { output += [stringData] } return output } } fileprivate extension NetworkLoggerPlugin { static func reversedPrint(_ separator: String, terminator: String, items: Any...) { for item in items { print(item, separator: separator, terminator: terminator) } } } ================================================ FILE: Pods/Moya/Sources/Moya/Response.swift ================================================ import Foundation /// Represents a response to a `MoyaProvider.request`. public final class Response: CustomDebugStringConvertible, Equatable { public let statusCode: Int public let data: Data public let request: URLRequest? public let response: HTTPURLResponse? /// Initialize a new `Response`. public init(statusCode: Int, data: Data, request: URLRequest? = nil, response: HTTPURLResponse? = nil) { self.statusCode = statusCode self.data = data self.request = request self.response = response } /// A text description of the `Response`. public var description: String { return "Status Code: \(statusCode), Data Length: \(data.count)" } /// A text description of the `Response`. Suitable for debugging. public var debugDescription: String { return description } public static func == (lhs: Response, rhs: Response) -> Bool { return lhs.statusCode == rhs.statusCode && lhs.data == rhs.data && lhs.response == rhs.response } } public extension Response { /** Returns the `Response` if the `statusCode` falls within the specified range. - parameters: - statusCodes: The range of acceptable status codes. - throws: `MoyaError.statusCode` when others are encountered. */ public func filter(statusCodes: ClosedRange) throws -> Response { guard statusCodes.contains(statusCode) else { throw MoyaError.statusCode(self) } return self } /** Returns the `Response` if it has the specified `statusCode`. - parameters: - statusCode: The acceptable status code. - throws: `MoyaError.statusCode` when others are encountered. */ public func filter(statusCode: Int) throws -> Response { return try filter(statusCodes: statusCode...statusCode) } /** Returns the `Response` if the `statusCode` falls within the range 200 - 299. - throws: `MoyaError.statusCode` when others are encountered. */ public func filterSuccessfulStatusCodes() throws -> Response { return try filter(statusCodes: 200...299) } /** Returns the `Response` if the `statusCode` falls within the range 200 - 399. - throws: `MoyaError.statusCode` when others are encountered. */ public func filterSuccessfulStatusAndRedirectCodes() throws -> Response { return try filter(statusCodes: 200...399) } /// Maps data received from the signal into a UIImage. func mapImage() throws -> Image { guard let image = Image(data: data) else { throw MoyaError.imageMapping(self) } return image } /// Maps data received from the signal into a JSON object. func mapJSON(failsOnEmptyData: Bool = true) throws -> Any { do { return try JSONSerialization.jsonObject(with: data, options: .allowFragments) } catch { if data.count < 1 && !failsOnEmptyData { return NSNull() } throw MoyaError.jsonMapping(self) } } /// Maps data received from the signal into a String. /// /// - parameter atKeyPath: Optional key path at which to parse string. public func mapString(atKeyPath keyPath: String? = nil) throws -> String { if let keyPath = keyPath { // Key path was provided, try to parse string at key path guard let jsonDictionary = try mapJSON() as? NSDictionary, let string = jsonDictionary.value(forKeyPath: keyPath) as? String else { throw MoyaError.stringMapping(self) } return string } else { // Key path was not provided, parse entire response as string guard let string = String(data: data, encoding: .utf8) else { throw MoyaError.stringMapping(self) } return string } } /// Maps data received from the signal into a Decodable object. /// /// - parameter atKeyPath: Optional key path at which to parse object. /// - parameter using: A `JSONDecoder` instance which is used to decode data to an object. func map(_ type: D.Type, atKeyPath keyPath: String? = nil, using decoder: JSONDecoder = JSONDecoder()) throws -> D { let serializeToData: (Any) throws -> Data? = { (jsonObject) in guard JSONSerialization.isValidJSONObject(jsonObject) else { return nil } do { return try JSONSerialization.data(withJSONObject: jsonObject) } catch { throw MoyaError.jsonMapping(self) } } let jsonData: Data if let keyPath = keyPath { guard let jsonObject = (try mapJSON() as? NSDictionary)?.value(forKeyPath: keyPath) else { throw MoyaError.jsonMapping(self) } if let data = try serializeToData(jsonObject) { jsonData = data } else { let wrappedJsonObject = ["value": jsonObject] let wrappedJsonData: Data if let data = try serializeToData(wrappedJsonObject) { wrappedJsonData = data } else { throw MoyaError.jsonMapping(self) } do { return try decoder.decode(DecodableWrapper.self, from: wrappedJsonData).value } catch let error { throw MoyaError.objectMapping(error, self) } } } else { jsonData = data } do { return try decoder.decode(D.self, from: jsonData) } catch let error { throw MoyaError.objectMapping(error, self) } } } private struct DecodableWrapper: Decodable { let value: T } ================================================ FILE: Pods/Moya/Sources/Moya/TargetType.swift ================================================ import Foundation /// The protocol used to define the specifications necessary for a `MoyaProvider`. public protocol TargetType { /// The target's base `URL`. var baseURL: URL { get } /// The path to be appended to `baseURL` to form the full `URL`. var path: String { get } /// The HTTP method used in the request. var method: Moya.Method { get } /// Provides stub data for use in testing. var sampleData: Data { get } /// The type of HTTP task to be performed. var task: Task { get } /// Whether or not to perform Alamofire validation. Defaults to `false`. var validate: Bool { get } /// The headers to be used in the request. var headers: [String: String]? { get } } public extension TargetType { var validate: Bool { return false } } ================================================ FILE: Pods/Moya/Sources/Moya/Task.swift ================================================ import Foundation /// Represents an HTTP task. public enum Task { /// A request with no additional data. case requestPlain /// A requests body set with data. case requestData(Data) /// A request body set with `Encodable` type case requestJSONEncodable(Encodable) /// A requests body set with encoded parameters. case requestParameters(parameters: [String: Any], encoding: ParameterEncoding) /// A requests body set with data, combined with url parameters. case requestCompositeData(bodyData: Data, urlParameters: [String: Any]) /// A requests body set with encoded parameters combined with url parameters. case requestCompositeParameters(bodyParameters: [String: Any], bodyEncoding: ParameterEncoding, urlParameters: [String: Any]) /// A file upload task. case uploadFile(URL) /// A "multipart/form-data" upload task. case uploadMultipart([MultipartFormData]) /// A "multipart/form-data" upload task combined with url parameters. case uploadCompositeMultipart([MultipartFormData], urlParameters: [String: Any]) /// A file download task to a destination. case downloadDestination(DownloadDestination) /// A file download task to a destination with extra parameters using the given encoding. case downloadParameters(parameters: [String: Any], encoding: ParameterEncoding, destination: DownloadDestination) } ================================================ FILE: Pods/Moya/Sources/Moya/URL+Moya.swift ================================================ import Foundation public extension URL { /// Initialize URL from Moya's `TargetType`. init(target: T) { // When a TargetType's path is empty, URL.appendingPathComponent may introduce trailing /, which may not be wanted in some cases // See: https://github.com/Moya/Moya/pull/1053 // And: https://github.com/Moya/Moya/issues/1049 if target.path.isEmpty { self = target.baseURL } else { self = target.baseURL.appendingPathComponent(target.path) } } } ================================================ FILE: Pods/Moya/Sources/Moya/URLRequest+Encoding.swift ================================================ import Foundation internal extension URLRequest { mutating func encoded(encodable: Encodable) throws -> URLRequest { do { let encodable = AnyEncodable(encodable) httpBody = try JSONEncoder().encode(encodable) let contentTypeHeaderName = "Content-Type" if value(forHTTPHeaderField: contentTypeHeaderName) == nil { setValue("application/json", forHTTPHeaderField: contentTypeHeaderName) } return self } catch { throw MoyaError.encodableMapping(error) } } func encoded(parameters: [String: Any], parameterEncoding: ParameterEncoding) throws -> URLRequest { do { return try parameterEncoding.encode(self, with: parameters) } catch { throw MoyaError.parameterEncoding(error) } } } ================================================ FILE: Pods/NSObject+Rx/HasDisposeBag.swift ================================================ import Foundation import RxSwift import ObjectiveC fileprivate var disposeBagContext: UInt8 = 0 /// each HasDisposeBag offers a unique RxSwift DisposeBag instance public protocol HasDisposeBag: class { /// a unique RxSwift DisposeBag instance var disposeBag: DisposeBag { get set } } extension HasDisposeBag { func synchronizedBag( _ action: () -> T) -> T { objc_sync_enter(self) let result = action() objc_sync_exit(self) return result } public var disposeBag: DisposeBag { get { return synchronizedBag { if let disposeObject = objc_getAssociatedObject(self, &disposeBagContext) as? DisposeBag { return disposeObject } let disposeObject = DisposeBag() objc_setAssociatedObject(self, &disposeBagContext, disposeObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return disposeObject } } set { synchronizedBag { objc_setAssociatedObject(self, &disposeBagContext, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } } ================================================ FILE: Pods/NSObject+Rx/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Ash Furrow 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. ================================================ FILE: Pods/NSObject+Rx/NSObject+Rx.swift ================================================ import Foundation import RxSwift import ObjectiveC fileprivate var disposeBagContext: UInt8 = 0 extension Reactive where Base: AnyObject { func synchronizedBag( _ action: () -> T) -> T { objc_sync_enter(self.base) let result = action() objc_sync_exit(self.base) return result } } public extension Reactive where Base: AnyObject { /// a unique DisposeBag that is related to the Reactive.Base instance only for Reference type public var disposeBag: DisposeBag { get { return synchronizedBag { if let disposeObject = objc_getAssociatedObject(base, &disposeBagContext) as? DisposeBag { return disposeObject } let disposeObject = DisposeBag() objc_setAssociatedObject(base, &disposeBagContext, disposeObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return disposeObject } } set { synchronizedBag { objc_setAssociatedObject(base, &disposeBagContext, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } } ================================================ FILE: Pods/NSObject+Rx/Readme.md ================================================ [![Build Status](https://travis-ci.org/RxSwiftCommunity/NSObject-Rx.svg?branch=master)](https://travis-ci.org/RxSwiftCommunity/NSObject-Rx) NSObject+Rx =========== If you're using [RxSwift](https://github.com/ReactiveX/RxSwift), you've probably encountered the following code more than a few times. ```swift class MyObject: Whatever { let disposeBag = DisposeBag() ... } ``` You're actually not the only one; it has been typed many, many times. [![Search screenshot showing many, many results.](assets/screenshot.png)](https://github.com/search?q=let+disposeBag+%3D+DisposeBag%28%29&type=Code&utf8=✓) Instead of adding a new property to every object, use this library to add it for you, to any subclass of `NSObject`. ```swift thing .bind(to: otherThing) .disposed(by: rx.disposeBag) ``` Sweet. It'll work just like a property: when the instance is deinit'd, the `DisposeBag` gets disposed. It's also a read/write property, so you can use your own, too. If you want to add a DisposeBag to an Object that does not inherit from NSObject, you can also implement the protocol `HasDisposeBag`, and you're good to go. This protocol provides a default DisposeBag called `disposeBag`. Installing ---------- #### CocoaPods Add to your `Podfile`: ```ruby pod 'NSObject+Rx' ``` And that'll be 👌 #### Carthage Add to `Cartfile`: ``` github "RxSwiftCommunity/NSObject-Rx" ``` Add frameworks to your project (no need to "copy items if needed") Run `carthage update` or `carthage update --platform ios` if you target iOS only Add run script build phase `/usr/local/bin/carthage copy-frameworks` with input files being: ``` $(SRCROOT)/Carthage/Build/iOS/RxSwift.framework $(SRCROOT)/Carthage/Build/iOS/NSObject_Rx.framework ``` And rule ✌️ Contributing ------------ Source files are in the root directory. We use CocoaPods to develop, check out the unit tests in the Demo project. License ------- MIT obvs. ![Tim Cook dancing to the sound of a permissive license.](http://i.imgur.com/mONiWzj.gif) ================================================ FILE: Pods/ObjectMapper/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Hearst 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. ================================================ FILE: Pods/ObjectMapper/README-CN.md ================================================ # ObjectMapper-CN-Guide > 文档由Swift老司机活动中心负责翻译,欢迎关注[@SwiftOldDriver](http://weibo.com/6062089411)。翻译有问题可以到 [ObjectMapper-CN-Guide](https://github.com/SwiftOldDriver/ObjectMapper-CN-Guide) 提 PR。 [ObjectMapper](https://github.com/Hearst-DD/ObjectMapper) 是一个使用 Swift 编写的用于 model 对象(类和结构体)和 JSON 之间转换的框架。 - [特性](#特性) - [基础使用方法](#基础使用方法) - [映射嵌套对象](#映射嵌套对象) - [自定义转换规则](#自定义转换规则) - [继承](#继承) - [泛型对象](#泛型对象) - [映射时的上下文对象](#映射时的上下文对象) - [ObjectMapper + Alamofire](#objectmapper--alamofire) - [ObjectMapper + Realm](#objectmapper--realm) - [待完成](#待完成) - [安装](#安装) # 特性: - 把 JSON 映射成对象 - 把对象映射 JSON - 支持嵌套对象 (单独的成员变量、在数组或字典中都可以) - 在转换过程支持自定义规则 - 支持结构体( Struct ) - [Immutable support](#immutablemappable-protocol-beta) (目前还在 beta ) # 基础使用方法 为了支持映射,类或者结构体只需要实现```Mappable```协议。这个协议包含以下方法: ```swift init?(map: Map) mutating func mapping(map: Map) ``` ObjectMapper使用自定义的```<-``` 运算符来声明成员变量和 JSON 的映射关系。 ```swift class User: Mappable { var username: String? var age: Int? var weight: Double! var array: [AnyObject]? var dictionary: [String : AnyObject] = [:] var bestFriend: User? // 嵌套的 User 对象 var friends: [User]? // Users 的数组 var birthday: NSDate? required init?(map: Map) { } // Mappable func mapping(map: Map) { username <- map["username"] age <- map["age"] weight <- map["weight"] array <- map["arr"] dictionary <- map["dict"] bestFriend <- map["best_friend"] friends <- map["friends"] birthday <- (map["birthday"], DateTransform()) } } struct Temperature: Mappable { var celsius: Double? var fahrenheit: Double? init?(map: Map) { } mutating func mapping(map: Map) { celsius <- map["celsius"] fahrenheit <- map["fahrenheit"] } } ``` 一旦你的对象实现了 `Mappable`, ObjectMapper就可以让你轻松的实现和 JSON 之间的转换。 把 JSON 字符串转成 model 对象: ```swift let user = User(JSONString: JSONString) ``` 把一个 model 转成 JSON 字符串: ```swift let JSONString = user.toJSONString(prettyPrint: true) ``` 也可以使用`Mapper.swift`类来完成转换(这个类还额外提供了一些函数来处理一些特殊的情况: ```swift // 把 JSON 字符串转成 Model let user = Mapper().map(JSONString: JSONString) // 根据 Model 生成 JSON 字符串 let JSONString = Mapper().toJSONString(user, prettyPrint: true) ``` ObjectMapper支持以下的类型映射到对象中: - `Int` - `Bool` - `Double` - `Float` - `String` - `RawRepresentable` (枚举) - `Array` - `Dictionary` - `Object` - `Array` - `Array>` - `Set` - `Dictionary` - `Dictionary>` - 以上所有的 Optional 类型 - 以上所有的隐式强制解包类型(Implicitly Unwrapped Optional) ## `Mappable` 协议 #### `mutating func mapping(map: Map)` 所有的映射最后都会调用到这个函数。当解析 JSON 时,这个函数会在对象创建成功后被执行。当生成 JSON 时就只有这个函数会被对象调用。 #### `init?(map: Map)` 这个可失败的初始化函数是 ObjectMapper 创建对象的时候使用的。开发者可以通过这个函数在映射前校验 JSON 。如果在这个方法里返回 nil 就不会执行 `mapping` 函数。可以通过传入的保存着 JSON 的 `Map` 对象进行校验: ```swift required init?(map: Map){ // 检查 JSON 里是否有一定要有的 "name" 属性 if map.JSONDictionary["name"] == nil { return nil } } ``` ## `StaticMappable` 协议 `StaticMappable` 是 `Mappable` 之外的另一种选择。 这个协议可以让开发者通过一个静态函数初始化对象而不是通过 `init?(map: Map)`。 注意: `StaticMappable` 和 `Mappable` 都继承了 `BaseMappable` 协议。 `BaseMappable` 协议声明了 `mapping(map: Map)` 函数。 #### `static func objectForMapping(map: Map) -> BaseMappable?` ObjectMapper 使用这个函数获取对象后进行映射。开发者需要在这个函数里返回一个实现 `BaseMappable` 对象的实例。这个函数也可以用于: - 在对象进行映射前校验 JSON - 提供一个缓存过的对象用于映射 - 返回另外一种类型的对象(当然是必须实现了 BaseMappable)用于映射。比如你可能通过检查 JSON 推断出用于映射的对象 ([看这个例子](https://github.com/Hearst-DD/ObjectMapper/blob/master/ObjectMapperTests/ClassClusterTests.swift#L62))。 如果你需要在 extension 里实现 ObjectMapper,你需要选择这个协议而不是 `Mappable` 。 ## `ImmutableMappable` Protocol (Beta) > ⚠️ 这个特性还处于 Beta 阶段。正式发布时 API 可能会完全不同。 使用 `ImmutableMappable` 可以映射不可变的属性。下面的表格展示了 `ImmutableMappable` 和 `Mappable` 的不同:
ImmutableMappable Mappable
Properties
let id: Int
let name: String?
var id: Int!
var name: String?
JSON -> Model
init(map: Map) throws {
  id   = try map.value("id")
  name = try? map.value("name")
}
mutating func mapping(map: Map) {
  id   <- map["id"]
  name <- map["name"]
}
Model -> JSON
mutating func mapping(map: Map) {
  id   >>> map["id"]
  name >>> map["name"]
}
mutating func mapping(map: Map) {
  id   <- map["id"]
  name <- map["name"]
}
Initializing
try User(JSONString: JSONString)
User(JSONString: JSONString)
#### `init(map: Map) throws` 这个可能抛出异常的初始化函数用于在提供的 `Map` 里映射不可变属性。每个不可变的初始化属性都要在这个初始化函数里初始化。 当发生下列情况时初始化函数会抛出一个错误: - `Map` 根据提供的键名获取不到对应值 - `Map` 使用 `Transform` 后没有得到值 `ImmutableMappable` 使用 `Map.value(_:using:)` 方法从 `Map` 中获取值。因为可能抛出异常,这个方法在使用时需要使用 `try` 关键字。 `Optional` 的属性可以简单的用 `try?` 处理。 ```swift init(map: Map) throws { name = try map.value("name") // throws an error when it fails createdAt = try map.value("createdAt", using: DateTransform()) // throws an error when it fails updatedAt = try? map.value("updatedAt", using: DateTransform()) // optional posts = (try? map.value("posts")) ?? [] // optional + default value } ``` #### `mutating func mapping(map: Map)` 这个方法是在 Model 转回 JSON 时调用的。因为不可变的属性不能被 `<-` 映射,所以映射回来时需要使用 `>>>` 。 ```swift mutating func mapping(map: Map) { name >>> map["name"] createdAt >>> (map["createdAt"], DateTransform()) updatedAt >>> (map["updatedAt"], DateTransform()) posts >>> map["posts"] } ``` # 轻松映射嵌套对象 ObjectMapper 支持使用点语法来轻松实现嵌套对象的映射。比如有如下的 JSON 字符串: ```json "distance" : { "text" : "102 ft", "value" : 31 } ``` 你可以通过这种写法直接访问到嵌套对象: ```swift func mapping(map: Map) { distance <- map["distance.value"] } ``` 嵌套的键名也支持访问数组中的值。如果有一个返回的 JSON 是一个包含 distance 的数组,可以通过这种写法访问: ``` distance <- map["distances.0.value"] ``` 如果你的键名刚好含有 `.` 符号,你需要特别声明关闭上面提到的获取嵌套对象功能: ```swift func mapping(map: Map) { identifier <- map["app.identifier", nested: false] } ``` 如果刚好有嵌套的对象的键名还有 `.` ,可以在中间加入一个自定义的分割符([#629](https://github.com/Hearst-DD/ObjectMapper/pull/629)): ```swift func mapping(map: Map) { appName <- map["com.myapp.info->com.myapp.name", delimiter: "->"] } ``` 这种情况的 JSON 是这样的: ```json "com.myapp.info" : { "com.myapp.name" : "SwiftOldDriver" } ``` # 自定义转换规则 ObjectMapper 也支持在映射时自定义转换规则。如果要使用自定义转换,创建一个 tuple(元祖)包含 ```map["field_name"]``` 和你要使用的变换放在 ```<-``` 的右边: ```swift birthday <- (map["birthday"], DateTransform()) ``` 当解析 JSON 时上面的转换会把 JSON 里面的 Int 值转成一个 NSDate ,如果是对象转为 JSON 时,则会把 NSDate 对象转成 Int 值。 只要实现```TransformType``` 协议就可以轻松的创建自定义的转换规则: ```swift public protocol TransformType { associatedtype Object associatedtype JSON func transformFromJSON(_ value: Any?) -> Object? func transformToJSON(_ value: Object?) -> JSON? } ``` ### TransformOf 大多数情况下你都可以使用框架提供的转换类 ```TransformOf``` 来快速的实现一个期望的转换。 ```TransformOf``` 的初始化需要两个类型和两个闭包。两个类型声明了转换的目标类型和源类型,闭包则实现具体转换逻辑。 举个例子,如果你想要把一个 JSON 字符串转成 Int ,你可以像这样使用 ```TransformOf``` : ```swift let transform = TransformOf(fromJSON: { (value: String?) -> Int? in // 把值从 String? 转成 Int? return Int(value!) }, toJSON: { (value: Int?) -> String? in // 把值从 Int? 转成 String? if let value = value { return String(value) } return nil }) id <- (map["id"], transform) ``` 这是一种更省略的写法: ```swift id <- (map["id"], TransformOf(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } })) ``` # 继承 实现了 ```Mappable``` 协议的类可以容易的被继承。当继承一个 mappable 的类时,使用这样的结构: ```swift class Base: Mappable { var base: String? required init?(map: Map) { } func mapping(map: Map) { base <- map["base"] } } class Subclass: Base { var sub: String? required init?(map: Map) { super.init(map) } override func mapping(map: Map) { super.mapping(map) sub <- map["sub"] } } ``` 注意确认子类中的实现调用了父类中正确的初始化器和映射函数。 # 泛型对象 ObjectMapper 可以处理泛型只要这个泛型也实现了`Mappable`协议。看这个例子: ```swift class Result: Mappable { var result: T? required init?(map: Map){ } func mapping(map: Map) { result <- map["result"] } } let result = Mapper>().map(JSON) ``` # 映射时的上下文对象 `Map` 是在映射时传入的对象,带有一个 optional `MapContext` 对象,开发者可以通过使用这个对象在映射时传入一些信息。 为了使用这个特性,需要先创建一个对象实现了 `MapContext` 协议(这个协议是空的),然后在初始化时传入 `Mapper` 中。 ```swift struct Context: MapContext { var importantMappingInfo = "映射时需要知道的额外信息" } class User: Mappable { var name: String? required init?(map: Map){ } func mapping(map: Map){ if let context = map.context as? Context { // 获取到额外的信息 } } } let context = Context() let user = Mapper(context: context).map(JSONString) ``` # ObjectMapper + Alamofire 如果网络层你使用的是 [Alamofire](https://github.com/Alamofire/Alamofire) ,并且你希望把返回的结果转换成 Swift 对象,你可以使用 [AlamofireObjectMapper](https://github.com/tristanhimmelman/AlamofireObjectMapper) 。这是一个使用 ObjectMapper 实现的把返回的 JSON 自动转成 Swift 对象的 Alamofire 的扩展。 # ObjectMapper + Realm ObjectMapper 可以和 Realm 一起配合使用。使用下面的声明结构就可以使用 ObjectMapper 生成 Realm 对象: ```swift class Model: Object, Mappable { dynamic var name = "" required convenience init?(map: Map) { self.init() } func mapping(map: Map) { name <- map["name"] } } ``` 如果你想要序列化相关联的 RealmObject,你可以使用 [ObjectMapper+Realm](https://github.com/jakenberg/ObjectMapper-Realm)。这是一个简单的 Realm 扩展,用于把任意的 JSON 序列化成 Realm 的类(ealm's List class。) 注意:使用 ObjectMappers 的 `toJSON` 函数来生成 JSON 字符串只在 Realm 的写事务中有效(write transaction)。这是因为 ObjectMapper 在解析和生成时在映射函数( `<-` )中使用 `inout` 作为标记( flag )。Realm 会检测到标记并且强制要求 `toJSON` 函数只能在一个写的事务中调用,即使这个对象并没有被修改。 # 待完成 - 改善错误的处理。可能使用 `throws` 来处理。 - 相关类的文档完善 # 安装 ### Cocoapods 如果你的项目使用 [CocoaPods 0.36 及以上](http://blog.cocoapods.org/Pod-Authors-Guide-to-CocoaPods-Frameworks/) 的版本,你可以把下面内容添加到在 `Podfile` 中,将 ObjectMapper 添加到你的项目中: ```ruby pod 'ObjectMapper', '~> 2.2' ``` ### Carthage 如果你的项目使用 [Carthage](https://github.com/Carthage/Carthage) ,你可以把下面的内容添加到 `Cartfile` 中,将 ObjectMapper 的依赖到你的项目中: ``` github "Hearst-DD/ObjectMapper" ~> 2.2 ``` ### Swift Package Manager 如果你的项目使用 [Swift Package Manager](https://swift.org/package-manager/) ,那么你可以把下面内容添加到 `Package.swift` 中的 `dependencies` 数组中,将 ObjectMapper 的依赖到你的项目中: ```swift .Package(url: "https://github.com/Hearst-DD/ObjectMapper.git", majorVersion: 2, minor: 2), ``` ### Submodule 此外,ObjectMapper 也可以作为一个 submodule 添加到项目中: 1. 打开终端,使用 `cd` 命令进入项目文件的根目录下,然后在终端中输入 `git submodule add https://github.com/Hearst-DD/ObjectMapper.git` ,把 ObjectMapper 作为项目的一个 [submodule](http://git-scm.com/docs/git-submodule) 添加进来。 2. 打开 `ObjectMapper` 文件,并将 `ObjectMapper.xcodeproj` 拖进你 app 项目的文件导航中。 3. 在 Xcode 中,文件导航中点击蓝色项目图标进入到 target 配置界面,在侧边栏的 "TARGETS" 下选择主工程对应的target。 4. 确保 `ObjectMapper.framework` 的部署版本( deployment target )和主工程的部署版本保持一致。 5. 在配置界面的顶部选项栏中,打开 "Build Phases" 面板。 6. 展开 "Target Dependencies" 组,并添加 `ObjectMapper.framework` 。 7. 点击面板左上角的 `+` 按钮,选择 "New Copy Files Phase"。将这个阶段重命名为 "Copy Frameworks",设置 "Destination" 为 "Frameworks",最后添加 `ObjectMapper.framework` 。 ================================================ FILE: Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift ================================================ // // CustomDateFormatTransform.swift // ObjectMapper // // Created by Dan McCracken on 3/8/15. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation open class CustomDateFormatTransform: DateFormatterTransform { public init(formatString: String) { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = formatString super.init(dateFormatter: formatter) } } ================================================ FILE: Pods/ObjectMapper/Sources/DataTransform.swift ================================================ // // DataTransform.swift // ObjectMapper // // Created by Yagrushkin, Evgeny on 8/30/16. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation open class DataTransform: TransformType { public typealias Object = Data public typealias JSON = String public init() {} open func transformFromJSON(_ value: Any?) -> Data? { guard let string = value as? String else{ return nil } return Data(base64Encoded: string) } open func transformToJSON(_ value: Data?) -> String? { guard let data = value else{ return nil } return data.base64EncodedString() } } ================================================ FILE: Pods/ObjectMapper/Sources/DateFormatterTransform.swift ================================================ // // DateFormatterTransform.swift // ObjectMapper // // Created by Tristan Himmelman on 2015-03-09. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation open class DateFormatterTransform: TransformType { public typealias Object = Date public typealias JSON = String public let dateFormatter: DateFormatter public init(dateFormatter: DateFormatter) { self.dateFormatter = dateFormatter } open func transformFromJSON(_ value: Any?) -> Date? { if let dateString = value as? String { return dateFormatter.date(from: dateString) } return nil } open func transformToJSON(_ value: Date?) -> String? { if let date = value { return dateFormatter.string(from: date) } return nil } } ================================================ FILE: Pods/ObjectMapper/Sources/DateTransform.swift ================================================ // // DateTransform.swift // ObjectMapper // // Created by Tristan Himmelman on 2014-10-13. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation open class DateTransform: TransformType { public typealias Object = Date public typealias JSON = Double public init() {} open func transformFromJSON(_ value: Any?) -> Date? { if let timeInt = value as? Double { return Date(timeIntervalSince1970: TimeInterval(timeInt)) } if let timeStr = value as? String { return Date(timeIntervalSince1970: TimeInterval(atof(timeStr))) } return nil } open func transformToJSON(_ value: Date?) -> Double? { if let date = value { return Double(date.timeIntervalSince1970) } return nil } } ================================================ FILE: Pods/ObjectMapper/Sources/DictionaryTransform.swift ================================================ // // DictionaryTransform.swift // ObjectMapper // // Created by Milen Halachev on 7/20/16. // Copyright © 2016 hearst. All rights reserved. // import Foundation ///Transforms [String: AnyObject] <-> [Key: Value] where Key is RawRepresentable as String, Value is Mappable public struct DictionaryTransform: TransformType where Key: Hashable, Key: RawRepresentable, Key.RawValue == String, Value: Mappable { public init() { } public func transformFromJSON(_ value: Any?) -> [Key: Value]? { guard let json = value as? [String: Any] else { return nil } let result = json.reduce([:]) { (result, element) -> [Key: Value] in guard let key = Key(rawValue: element.0), let valueJSON = element.1 as? [String: Any], let value = Value(JSON: valueJSON) else { return result } var result = result result[key] = value return result } return result } public func transformToJSON(_ value: [Key: Value]?) -> Any? { let result = value?.reduce([:]) { (result, element) -> [String: Any] in let key = element.0.rawValue let value = element.1.toJSON() var result = result result[key] = value return result } return result } } ================================================ FILE: Pods/ObjectMapper/Sources/EnumOperators.swift ================================================ // // EnumOperators.swift // ObjectMapper // // Created by Tristan Himmelman on 2016-09-26. // Copyright © 2016 hearst. All rights reserved. // import Foundation // MARK:- Raw Representable types /// Object of Raw Representable type public func <- (left: inout T, right: Map) { left <- (right, EnumTransform()) } public func >>> (left: T, right: Map) { left >>> (right, EnumTransform()) } /// Optional Object of Raw Representable type public func <- (left: inout T?, right: Map) { left <- (right, EnumTransform()) } public func >>> (left: T?, right: Map) { left >>> (right, EnumTransform()) } /// Implicitly Unwrapped Optional Object of Raw Representable type public func <- (left: inout T!, right: Map) { left <- (right, EnumTransform()) } // MARK:- Arrays of Raw Representable type /// Array of Raw Representable object public func <- (left: inout [T], right: Map) { left <- (right, EnumTransform()) } public func >>> (left: [T], right: Map) { left >>> (right, EnumTransform()) } /// Array of Raw Representable object public func <- (left: inout [T]?, right: Map) { left <- (right, EnumTransform()) } public func >>> (left: [T]?, right: Map) { left >>> (right, EnumTransform()) } /// Array of Raw Representable object public func <- (left: inout [T]!, right: Map) { left <- (right, EnumTransform()) } // MARK:- Dictionaries of Raw Representable type /// Dictionary of Raw Representable object public func <- (left: inout [String: T], right: Map) { left <- (right, EnumTransform()) } public func >>> (left: [String: T], right: Map) { left >>> (right, EnumTransform()) } /// Dictionary of Raw Representable object public func <- (left: inout [String: T]?, right: Map) { left <- (right, EnumTransform()) } public func >>> (left: [String: T]?, right: Map) { left >>> (right, EnumTransform()) } /// Dictionary of Raw Representable object public func <- (left: inout [String: T]!, right: Map) { left <- (right, EnumTransform()) } ================================================ FILE: Pods/ObjectMapper/Sources/EnumTransform.swift ================================================ // // EnumTransform.swift // ObjectMapper // // Created by Kaan Dedeoglu on 3/20/15. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation open class EnumTransform: TransformType { public typealias Object = T public typealias JSON = T.RawValue public init() {} open func transformFromJSON(_ value: Any?) -> T? { if let raw = value as? T.RawValue { return T(rawValue: raw) } return nil } open func transformToJSON(_ value: T?) -> T.RawValue? { if let obj = value { return obj.rawValue } return nil } } ================================================ FILE: Pods/ObjectMapper/Sources/FromJSON.swift ================================================ // // FromJSON.swift // ObjectMapper // // Created by Tristan Himmelman on 2014-10-09. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. internal final class FromJSON { /// Basic type class func basicType(_ field: inout FieldType, object: FieldType?) { if let value = object { field = value } } /// optional basic type class func optionalBasicType(_ field: inout FieldType?, object: FieldType?) { field = object } /// Implicitly unwrapped optional basic type class func optionalBasicType(_ field: inout FieldType!, object: FieldType?) { field = object } /// Mappable object class func object(_ field: inout N, map: Map) { if map.toObject { field = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field) } else if let value: N = Mapper(context: map.context).map(JSONObject: map.currentValue) { field = value } } /// Optional Mappable Object class func optionalObject(_ field: inout N?, map: Map) { if let f = field , map.toObject && map.currentValue != nil { field = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: f) } else { field = Mapper(context: map.context).map(JSONObject: map.currentValue) } } /// Implicitly unwrapped Optional Mappable Object class func optionalObject(_ field: inout N!, map: Map) { if let f = field , map.toObject && map.currentValue != nil { field = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: f) } else { field = Mapper(context: map.context).map(JSONObject: map.currentValue) } } /// mappable object array class func objectArray(_ field: inout Array, map: Map) { if let objects = Mapper(context: map.context).mapArray(JSONObject: map.currentValue) { field = objects } } /// optional mappable object array class func optionalObjectArray(_ field: inout Array?, map: Map) { if let objects: Array = Mapper(context: map.context).mapArray(JSONObject: map.currentValue) { field = objects } else { field = nil } } /// Implicitly unwrapped optional mappable object array class func optionalObjectArray(_ field: inout Array!, map: Map) { if let objects: Array = Mapper(context: map.context).mapArray(JSONObject: map.currentValue) { field = objects } else { field = nil } } /// mappable object array class func twoDimensionalObjectArray(_ field: inout Array>, map: Map) { if let objects = Mapper(context: map.context).mapArrayOfArrays(JSONObject: map.currentValue) { field = objects } } /// optional mappable 2 dimentional object array class func optionalTwoDimensionalObjectArray(_ field: inout Array>?, map: Map) { field = Mapper(context: map.context).mapArrayOfArrays(JSONObject: map.currentValue) } /// Implicitly unwrapped optional 2 dimentional mappable object array class func optionalTwoDimensionalObjectArray(_ field: inout Array>!, map: Map) { field = Mapper(context: map.context).mapArrayOfArrays(JSONObject: map.currentValue) } /// Dctionary containing Mappable objects class func objectDictionary(_ field: inout Dictionary, map: Map) { if map.toObject { field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field) } else { if let objects = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue) { field = objects } } } /// Optional dictionary containing Mappable objects class func optionalObjectDictionary(_ field: inout Dictionary?, map: Map) { if let f = field , map.toObject && map.currentValue != nil { field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: f) } else { field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue) } } /// Implicitly unwrapped Dictionary containing Mappable objects class func optionalObjectDictionary(_ field: inout Dictionary!, map: Map) { if let f = field , map.toObject && map.currentValue != nil { field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: f) } else { field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue) } } /// Dictionary containing Array of Mappable objects class func objectDictionaryOfArrays(_ field: inout Dictionary, map: Map) { if let objects = Mapper(context: map.context).mapDictionaryOfArrays(JSONObject: map.currentValue) { field = objects } } /// Optional Dictionary containing Array of Mappable objects class func optionalObjectDictionaryOfArrays(_ field: inout Dictionary?, map: Map) { field = Mapper(context: map.context).mapDictionaryOfArrays(JSONObject: map.currentValue) } /// Implicitly unwrapped Dictionary containing Array of Mappable objects class func optionalObjectDictionaryOfArrays(_ field: inout Dictionary!, map: Map) { field = Mapper(context: map.context).mapDictionaryOfArrays(JSONObject: map.currentValue) } /// mappable object Set class func objectSet(_ field: inout Set, map: Map) { if let objects = Mapper(context: map.context).mapSet(JSONObject: map.currentValue) { field = objects } } /// optional mappable object array class func optionalObjectSet(_ field: inout Set?, map: Map) { field = Mapper(context: map.context).mapSet(JSONObject: map.currentValue) } /// Implicitly unwrapped optional mappable object array class func optionalObjectSet(_ field: inout Set!, map: Map) { field = Mapper(context: map.context).mapSet(JSONObject: map.currentValue) } } ================================================ FILE: Pods/ObjectMapper/Sources/HexColorTransform.swift ================================================ // // HexColorTransform.swift // ObjectMapper // // Created by Vitaliy Kuzmenko on 10/10/16. // Copyright © 2016 hearst. All rights reserved. // #if os(iOS) || os(tvOS) || os(watchOS) import UIKit #elseif os(macOS) import Cocoa #endif #if os(iOS) || os(tvOS) || os(watchOS) || os(macOS) open class HexColorTransform: TransformType { #if os(iOS) || os(tvOS) || os(watchOS) public typealias Object = UIColor #else public typealias Object = NSColor #endif public typealias JSON = String var prefix: Bool = false var alpha: Bool = false public init(prefixToJSON: Bool = false, alphaToJSON: Bool = false) { alpha = alphaToJSON prefix = prefixToJSON } open func transformFromJSON(_ value: Any?) -> Object? { if let rgba = value as? String { if rgba.hasPrefix("#") { let index = rgba.index(rgba.startIndex, offsetBy: 1) let hex = String(rgba[index...]) return getColor(hex: hex) } else { return getColor(hex: rgba) } } return nil } open func transformToJSON(_ value: Object?) -> JSON? { if let value = value { return hexString(color: value) } return nil } fileprivate func hexString(color: Object) -> String { let comps = color.cgColor.components! let compsCount = color.cgColor.numberOfComponents let r: Int let g: Int var b: Int let a = Int(comps[compsCount - 1] * 255) if compsCount == 4 { // RGBA r = Int(comps[0] * 255) g = Int(comps[1] * 255) b = Int(comps[2] * 255) } else { // Grayscale r = Int(comps[0] * 255) g = Int(comps[0] * 255) b = Int(comps[0] * 255) } var hexString: String = "" if prefix { hexString = "#" } hexString += String(format: "%02X%02X%02X", r, g, b) if alpha { hexString += String(format: "%02X", a) } return hexString } fileprivate func getColor(hex: String) -> Object? { var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var alpha: CGFloat = 1.0 let scanner = Scanner(string: hex) var hexValue: CUnsignedLongLong = 0 if scanner.scanHexInt64(&hexValue) { switch (hex.count) { case 3: red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 blue = CGFloat(hexValue & 0x00F) / 15.0 case 4: red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 alpha = CGFloat(hexValue & 0x000F) / 15.0 case 6: red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 blue = CGFloat(hexValue & 0x0000FF) / 255.0 case 8: red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 alpha = CGFloat(hexValue & 0x000000FF) / 255.0 default: // Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8 return nil } } else { // "Scan hex error return nil } #if os(iOS) || os(tvOS) || os(watchOS) return UIColor(red: red, green: green, blue: blue, alpha: alpha) #else return NSColor(calibratedRed: red, green: green, blue: blue, alpha: alpha) #endif } } #endif ================================================ FILE: Pods/ObjectMapper/Sources/ISO8601DateTransform.swift ================================================ // // ISO8601DateTransform.swift // ObjectMapper // // Created by Jean-Pierre Mouilleseaux on 21 Nov 2014. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation public extension DateFormatter { public convenience init(withFormat format : String, locale : String) { self.init() self.locale = Locale(identifier: locale) dateFormat = format } } open class ISO8601DateTransform: DateFormatterTransform { static let reusableISODateFormatter = DateFormatter(withFormat: "yyyy-MM-dd'T'HH:mm:ssZZZZZ", locale: "en_US_POSIX") public init() { super.init(dateFormatter: ISO8601DateTransform.reusableISODateFormatter) } } ================================================ FILE: Pods/ObjectMapper/Sources/ImmutableMappable.swift ================================================ // // ImmutableMappble.swift // ObjectMapper // // Created by Suyeol Jeon on 23/09/2016. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. public protocol ImmutableMappable: BaseMappable { init(map: Map) throws } public extension ImmutableMappable { /// Implement this method to support object -> JSON transform. public func mapping(map: Map) {} /// Initializes object from a JSON String public init(JSONString: String, context: MapContext? = nil) throws { self = try Mapper(context: context).map(JSONString: JSONString) } /// Initializes object from a JSON Dictionary public init(JSON: [String: Any], context: MapContext? = nil) throws { self = try Mapper(context: context).map(JSON: JSON) } /// Initializes object from a JSONObject public init(JSONObject: Any, context: MapContext? = nil) throws { self = try Mapper(context: context).map(JSONObject: JSONObject) } } public extension Map { fileprivate func currentValue(for key: String, nested: Bool? = nil, delimiter: String = ".") -> Any? { let isNested = nested ?? key.contains(delimiter) return self[key, nested: isNested, delimiter: delimiter].currentValue } // MARK: Basic /// Returns a value or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let value = currentValue as? T else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '\(T.self)'", file: file, function: function, line: line) } return value } /// Returns a transformed value or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", using transform: Transform, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> Transform.Object { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let value = transform.transformFromJSON(currentValue) else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot transform to '\(Transform.Object.self)' using \(transform)", file: file, function: function, line: line) } return value } /// Returns a RawRepresentable type or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T { return try self.value(key, nested: nested, delimiter: delimiter, using: EnumTransform(), file: file, function: function, line: line) } /// Returns a `[RawRepresentable]` type or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [T] { return try self.value(key, nested: nested, delimiter: delimiter, using: EnumTransform(), file: file, function: function, line: line) } // MARK: BaseMappable /// Returns a `BaseMappable` object or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let JSONObject = currentValue else { throw MapError(key: key, currentValue: currentValue, reason: "Found unexpected nil value", file: file, function: function, line: line) } return try Mapper(context: context).mapOrFail(JSONObject: JSONObject) } // MARK: [BaseMappable] /// Returns a `[BaseMappable]` or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [T] { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let jsonArray = currentValue as? [Any] else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[Any]'", file: file, function: function, line: line) } return try jsonArray.map { JSONObject -> T in return try Mapper(context: context).mapOrFail(JSONObject: JSONObject) } } /// Returns a `[BaseMappable]` using transform or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", using transform: Transform, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [Transform.Object] { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let jsonArray = currentValue as? [Any] else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[Any]'", file: file, function: function, line: line) } return try jsonArray.map { json -> Transform.Object in guard let object = transform.transformFromJSON(json) else { throw MapError(key: "\(key)", currentValue: json, reason: "Cannot transform to '\(Transform.Object.self)' using \(transform)", file: file, function: function, line: line) } return object } } // MARK: [String: BaseMappable] /// Returns a `[String: BaseMappable]` or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [String: T] { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let jsonDictionary = currentValue as? [String: Any] else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[String: Any]'", file: file, function: function, line: line) } var value: [String: T] = [:] for (key, json) in jsonDictionary { value[key] = try Mapper(context: context).mapOrFail(JSONObject: json) } return value } /// Returns a `[String: BaseMappable]` using transform or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", using transform: Transform, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [String: Transform.Object] { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let jsonDictionary = currentValue as? [String: Any] else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[String: Any]'", file: file, function: function, line: line) } var value: [String: Transform.Object] = [:] for (key, json) in jsonDictionary { guard let object = transform.transformFromJSON(json) else { throw MapError(key: key, currentValue: json, reason: "Cannot transform to '\(Transform.Object.self)' using \(transform)", file: file, function: function, line: line) } value[key] = object } return value } // MARK: [[BaseMappable]] /// Returns a `[[BaseMappable]]` or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [[T]] { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let json2DArray = currentValue as? [[Any]] else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[[Any]]'", file: file, function: function, line: line) } return try json2DArray.map { jsonArray in try jsonArray.map { jsonObject -> T in return try Mapper(context: context).mapOrFail(JSONObject: jsonObject) } } } /// Returns a `[[BaseMappable]]` using transform or throws an error. public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", using transform: Transform, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [[Transform.Object]] { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let json2DArray = currentValue as? [[Any]] else { throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[[Any]]'", file: file, function: function, line: line) } return try json2DArray.map { jsonArray in try jsonArray.map { json -> Transform.Object in guard let object = transform.transformFromJSON(json) else { throw MapError(key: "\(key)", currentValue: json, reason: "Cannot transform to '\(Transform.Object.self)' using \(transform)", file: file, function: function, line: line) } return object } } } } public extension Mapper where N: ImmutableMappable { public func map(JSON: [String: Any]) throws -> N { return try self.mapOrFail(JSON: JSON) } public func map(JSONString: String) throws -> N { return try mapOrFail(JSONString: JSONString) } public func map(JSONObject: Any) throws -> N { return try mapOrFail(JSONObject: JSONObject) } // MARK: Array mapping functions public func mapArray(JSONArray: [[String: Any]]) throws -> [N] { return try JSONArray.flatMap(mapOrFail) } public func mapArray(JSONString: String) throws -> [N] { guard let JSONObject = Mapper.parseJSONString(JSONString: JSONString) else { throw MapError(key: nil, currentValue: JSONString, reason: "Cannot convert string into Any'") } return try mapArray(JSONObject: JSONObject) } public func mapArray(JSONObject: Any) throws -> [N] { guard let JSONArray = JSONObject as? [[String: Any]] else { throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[[String: Any]]'") } return try mapArray(JSONArray: JSONArray) } // MARK: Dictionary mapping functions public func mapDictionary(JSONString: String) throws -> [String: N] { guard let JSONObject = Mapper.parseJSONString(JSONString: JSONString) else { throw MapError(key: nil, currentValue: JSONString, reason: "Cannot convert string into Any'") } return try mapDictionary(JSONObject: JSONObject) } public func mapDictionary(JSONObject: Any?) throws -> [String: N] { guard let JSON = JSONObject as? [String: [String: Any]] else { throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: [String: Any]]''") } return try mapDictionary(JSON: JSON) } public func mapDictionary(JSON: [String: [String: Any]]) throws -> [String: N] { return try JSON.filterMap(mapOrFail) } // MARK: Dictinoary of arrays mapping functions public func mapDictionaryOfArrays(JSONObject: Any?) throws -> [String: [N]] { guard let JSON = JSONObject as? [String: [[String: Any]]] else { throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: [String: Any]]''") } return try mapDictionaryOfArrays(JSON: JSON) } public func mapDictionaryOfArrays(JSON: [String: [[String: Any]]]) throws -> [String: [N]] { return try JSON.filterMap { array -> [N] in try mapArray(JSONArray: array) } } // MARK: 2 dimentional array mapping functions public func mapArrayOfArrays(JSONObject: Any?) throws -> [[N]] { guard let JSONArray = JSONObject as? [[[String: Any]]] else { throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[[[String: Any]]]''") } return try JSONArray.map(mapArray) } } internal extension Mapper { internal func mapOrFail(JSON: [String: Any]) throws -> N { let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues) // Check if object is ImmutableMappable, if so use ImmutableMappable protocol for mapping if let klass = N.self as? ImmutableMappable.Type, var object = try klass.init(map: map) as? N { object.mapping(map: map) return object } // If not, map the object the standard way guard let value = self.map(JSON: JSON) else { throw MapError(key: nil, currentValue: JSON, reason: "Cannot map to '\(N.self)'") } return value } internal func mapOrFail(JSONString: String) throws -> N { guard let JSON = Mapper.parseJSONStringIntoDictionary(JSONString: JSONString) else { throw MapError(key: nil, currentValue: JSONString, reason: "Cannot parse into '[String: Any]'") } return try mapOrFail(JSON: JSON) } internal func mapOrFail(JSONObject: Any) throws -> N { guard let JSON = JSONObject as? [String: Any] else { throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: Any]'") } return try mapOrFail(JSON: JSON) } } ================================================ FILE: Pods/ObjectMapper/Sources/IntegerOperators.swift ================================================ // // IntegerOperators.swift // ObjectMapper // // Created by Suyeol Jeon on 17/02/2017. // Copyright © 2017 hearst. All rights reserved. // import Foundation // MARK: - Signed Integer /// SignedInteger mapping public func <- (left: inout T, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: let value: T = toSignedInteger(right.currentValue) ?? 0 FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } /// Optional SignedInteger mapping public func <- (left: inout T?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: let value: T? = toSignedInteger(right.currentValue) FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } /// ImplicitlyUnwrappedOptional SignedInteger mapping public func <- (left: inout T!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: let value: T! = toSignedInteger(right.currentValue) FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } // MARK: - Unsigned Integer /// UnsignedInteger mapping public func <- (left: inout T, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: let value: T = toUnsignedInteger(right.currentValue) ?? 0 FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } /// Optional UnsignedInteger mapping public func <- (left: inout T?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: let value: T? = toUnsignedInteger(right.currentValue) FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } /// ImplicitlyUnwrappedOptional UnsignedInteger mapping public func <- (left: inout T!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: let value: T! = toUnsignedInteger(right.currentValue) FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } // MARK: - Casting Utils /// Convert any value to `SignedInteger`. private func toSignedInteger(_ value: Any?) -> T? { guard let value = value, case let number as NSNumber = value else { return nil } if T.self == Int.self, let x = Int(exactly: number.int64Value) { return T.init(x) } if T.self == Int8.self, let x = Int8(exactly: number.int64Value) { return T.init(x) } if T.self == Int16.self, let x = Int16(exactly: number.int64Value) { return T.init(x) } if T.self == Int32.self, let x = Int32(exactly: number.int64Value) { return T.init(x) } if T.self == Int64.self, let x = Int64(exactly: number.int64Value) { return T.init(x) } return nil } /// Convert any value to `UnsignedInteger`. private func toUnsignedInteger(_ value: Any?) -> T? { guard let value = value, case let number as NSNumber = value else { return nil } if T.self == UInt.self, let x = UInt(exactly: number.uint64Value) { return T.init(x) } if T.self == UInt8.self, let x = UInt8(exactly: number.uint64Value) { return T.init(x) } if T.self == UInt16.self, let x = UInt16(exactly: number.uint64Value) { return T.init(x) } if T.self == UInt32.self, let x = UInt32(exactly: number.uint64Value) { return T.init(x) } if T.self == UInt64.self, let x = UInt64(exactly: number.uint64Value) { return T.init(x) } return nil } ================================================ FILE: Pods/ObjectMapper/Sources/Map.swift ================================================ // // Map.swift // ObjectMapper // // Created by Tristan Himmelman on 2015-10-09. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation /// MapContext is available for developers who wish to pass information around during the mapping process. public protocol MapContext { } /// A class used for holding mapping data public final class Map { public let mappingType: MappingType public internal(set) var JSON: [String: Any] = [:] public internal(set) var isKeyPresent = false public internal(set) var currentValue: Any? public internal(set) var currentKey: String? var keyIsNested = false public internal(set) var nestedKeyDelimiter: String = "." public var context: MapContext? public var shouldIncludeNilValues = false /// If this is set to true, toJSON output will include null values for any variables that are not set. public let toObject: Bool // indicates whether the mapping is being applied to an existing object public init(mappingType: MappingType, JSON: [String: Any], toObject: Bool = false, context: MapContext? = nil, shouldIncludeNilValues: Bool = false) { self.mappingType = mappingType self.JSON = JSON self.toObject = toObject self.context = context self.shouldIncludeNilValues = shouldIncludeNilValues } /// Sets the current mapper value and key. /// The Key paramater can be a period separated string (ex. "distance.value") to access sub objects. public subscript(key: String) -> Map { // save key and value associated to it return self[key, delimiter: ".", ignoreNil: false] } public subscript(key: String, delimiter delimiter: String) -> Map { let nested = key.contains(delimiter) return self[key, nested: nested, delimiter: delimiter, ignoreNil: false] } public subscript(key: String, nested nested: Bool) -> Map { return self[key, nested: nested, delimiter: ".", ignoreNil: false] } public subscript(key: String, nested nested: Bool, delimiter delimiter: String) -> Map { return self[key, nested: nested, delimiter: delimiter, ignoreNil: false] } public subscript(key: String, ignoreNil ignoreNil: Bool) -> Map { return self[key, delimiter: ".", ignoreNil: ignoreNil] } public subscript(key: String, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map { let nested = key.contains(delimiter) return self[key, nested: nested, delimiter: delimiter, ignoreNil: ignoreNil] } public subscript(key: String, nested nested: Bool, ignoreNil ignoreNil: Bool) -> Map { return self[key, nested: nested, delimiter: ".", ignoreNil: ignoreNil] } public subscript(key: String, nested nested: Bool, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map { // save key and value associated to it currentKey = key keyIsNested = nested nestedKeyDelimiter = delimiter if mappingType == .fromJSON { // check if a value exists for the current key // do this pre-check for performance reasons if nested == false { let object = JSON[key] let isNSNull = object is NSNull isKeyPresent = isNSNull ? true : object != nil currentValue = isNSNull ? nil : object } else { // break down the components of the key that are separated by . (isKeyPresent, currentValue) = valueFor(ArraySlice(key.components(separatedBy: delimiter)), dictionary: JSON) } // update isKeyPresent if ignoreNil is true if ignoreNil && currentValue == nil { isKeyPresent = false } } return self } public func value() -> T? { return currentValue as? T } } /// Fetch value from JSON dictionary, loop through keyPathComponents until we reach the desired object private func valueFor(_ keyPathComponents: ArraySlice, dictionary: [String: Any]) -> (Bool, Any?) { // Implement it as a tail recursive function. if keyPathComponents.isEmpty { return (false, nil) } if let keyPath = keyPathComponents.first { let object = dictionary[keyPath] if object is NSNull { return (true, nil) } else if keyPathComponents.count > 1, let dict = object as? [String: Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, dictionary: dict) } else if keyPathComponents.count > 1, let array = object as? [Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, array: array) } else { return (object != nil, object) } } return (false, nil) } /// Fetch value from JSON Array, loop through keyPathComponents them until we reach the desired object private func valueFor(_ keyPathComponents: ArraySlice, array: [Any]) -> (Bool, Any?) { // Implement it as a tail recursive function. if keyPathComponents.isEmpty { return (false, nil) } //Try to convert keypath to Int as index if let keyPath = keyPathComponents.first, let index = Int(keyPath) , index >= 0 && index < array.count { let object = array[index] if object is NSNull { return (true, nil) } else if keyPathComponents.count > 1, let array = object as? [Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, array: array) } else if keyPathComponents.count > 1, let dict = object as? [String: Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, dictionary: dict) } else { return (true, object) } } return (false, nil) } ================================================ FILE: Pods/ObjectMapper/Sources/MapError.swift ================================================ // // MapError.swift // ObjectMapper // // Created by Tristan Himmelman on 2016-09-26. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation public struct MapError: Error { public var key: String? public var currentValue: Any? public var reason: String? public var file: StaticString? public var function: StaticString? public var line: UInt? public init(key: String?, currentValue: Any?, reason: String?, file: StaticString? = nil, function: StaticString? = nil, line: UInt? = nil) { self.key = key self.currentValue = currentValue self.reason = reason self.file = file self.function = function self.line = line } } extension MapError: CustomStringConvertible { private var location: String? { guard let file = file, let function = function, let line = line else { return nil } let fileName = ((String(describing: file).components(separatedBy: "/").last ?? "").components(separatedBy: ".").first ?? "") return "\(fileName).\(function):\(line)" } public var description: String { let info: [(String, Any?)] = [ ("- reason", reason), ("- location", location), ("- key", key), ("- currentValue", currentValue), ] let infoString = info.map { "\($0.0): \($0.1 ?? "nil")" }.joined(separator: "\n") return "Got an error while mapping.\n\(infoString)" } } ================================================ FILE: Pods/ObjectMapper/Sources/Mappable.swift ================================================ // // Mappable.swift // ObjectMapper // // Created by Scott Hoyt on 10/25/15. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation /// BaseMappable should not be implemented directly. Mappable or StaticMappable should be used instead public protocol BaseMappable { /// This function is where all variable mappings should occur. It is executed by Mapper during the mapping (serialization and deserialization) process. mutating func mapping(map: Map) } public protocol Mappable: BaseMappable { /// This function can be used to validate JSON prior to mapping. Return nil to cancel mapping at this point init?(map: Map) } public protocol StaticMappable: BaseMappable { /// This is function that can be used to: /// 1) provide an existing cached object to be used for mapping /// 2) return an object of another class (which conforms to BaseMappable) to be used for mapping. For instance, you may inspect the JSON to infer the type of object that should be used for any given mapping static func objectForMapping(map: Map) -> BaseMappable? } public extension BaseMappable { /// Initializes object from a JSON String public init?(JSONString: String, context: MapContext? = nil) { if let obj: Self = Mapper(context: context).map(JSONString: JSONString) { self = obj } else { return nil } } /// Initializes object from a JSON Dictionary public init?(JSON: [String: Any], context: MapContext? = nil) { if let obj: Self = Mapper(context: context).map(JSON: JSON) { self = obj } else { return nil } } /// Returns the JSON Dictionary for the object public func toJSON() -> [String: Any] { return Mapper().toJSON(self) } /// Returns the JSON String for the object public func toJSONString(prettyPrint: Bool = false) -> String? { return Mapper().toJSONString(self, prettyPrint: prettyPrint) } } public extension Array where Element: BaseMappable { /// Initialize Array from a JSON String public init?(JSONString: String, context: MapContext? = nil) { if let obj: [Element] = Mapper(context: context).mapArray(JSONString: JSONString) { self = obj } else { return nil } } /// Initialize Array from a JSON Array public init(JSONArray: [[String: Any]], context: MapContext? = nil) { let obj: [Element] = Mapper(context: context).mapArray(JSONArray: JSONArray) self = obj } /// Returns the JSON Array public func toJSON() -> [[String: Any]] { return Mapper().toJSONArray(self) } /// Returns the JSON String for the object public func toJSONString(prettyPrint: Bool = false) -> String? { return Mapper().toJSONString(self, prettyPrint: prettyPrint) } } public extension Set where Element: BaseMappable { /// Initializes a set from a JSON String public init?(JSONString: String, context: MapContext? = nil) { if let obj: Set = Mapper(context: context).mapSet(JSONString: JSONString) { self = obj } else { return nil } } /// Initializes a set from JSON public init?(JSONArray: [[String: Any]], context: MapContext? = nil) { guard let obj = Mapper(context: context).mapSet(JSONArray: JSONArray) as Set? else { return nil } self = obj } /// Returns the JSON Set public func toJSON() -> [[String: Any]] { return Mapper().toJSONSet(self) } /// Returns the JSON String for the object public func toJSONString(prettyPrint: Bool = false) -> String? { return Mapper().toJSONString(self, prettyPrint: prettyPrint) } } ================================================ FILE: Pods/ObjectMapper/Sources/Mapper.swift ================================================ // // Mapper.swift // ObjectMapper // // Created by Tristan Himmelman on 2014-10-09. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation public enum MappingType { case fromJSON case toJSON } /// The Mapper class provides methods for converting Model objects to JSON and methods for converting JSON to Model objects public final class Mapper { public var context: MapContext? public var shouldIncludeNilValues = false /// If this is set to true, toJSON output will include null values for any variables that are not set. public init(context: MapContext? = nil, shouldIncludeNilValues: Bool = false){ self.context = context self.shouldIncludeNilValues = shouldIncludeNilValues } // MARK: Mapping functions that map to an existing object toObject /// Maps a JSON object to an existing Mappable object if it is a JSON dictionary, or returns the passed object as is public func map(JSONObject: Any?, toObject object: N) -> N { if let JSON = JSONObject as? [String: Any] { return map(JSON: JSON, toObject: object) } return object } /// Map a JSON string onto an existing object public func map(JSONString: String, toObject object: N) -> N { if let JSON = Mapper.parseJSONStringIntoDictionary(JSONString: JSONString) { return map(JSON: JSON, toObject: object) } return object } /// Maps a JSON dictionary to an existing object that conforms to Mappable. /// Usefull for those pesky objects that have crappy designated initializers like NSManagedObject public func map(JSON: [String: Any], toObject object: N) -> N { var mutableObject = object let map = Map(mappingType: .fromJSON, JSON: JSON, toObject: true, context: context, shouldIncludeNilValues: shouldIncludeNilValues) mutableObject.mapping(map: map) return mutableObject } //MARK: Mapping functions that create an object /// Map a JSON string to an object that conforms to Mappable public func map(JSONString: String) -> N? { if let JSON = Mapper.parseJSONStringIntoDictionary(JSONString: JSONString) { return map(JSON: JSON) } return nil } /// Maps a JSON object to a Mappable object if it is a JSON dictionary or NSString, or returns nil. public func map(JSONObject: Any?) -> N? { if let JSON = JSONObject as? [String: Any] { return map(JSON: JSON) } return nil } /// Maps a JSON dictionary to an object that conforms to Mappable public func map(JSON: [String: Any]) -> N? { let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues) if let klass = N.self as? StaticMappable.Type { // Check if object is StaticMappable if var object = klass.objectForMapping(map: map) as? N { object.mapping(map: map) return object } } else if let klass = N.self as? Mappable.Type { // Check if object is Mappable if var object = klass.init(map: map) as? N { object.mapping(map: map) return object } } else if let klass = N.self as? ImmutableMappable.Type { // Check if object is ImmutableMappable do { return try klass.init(map: map) as? N } catch let error { #if DEBUG let exception: NSException if let mapError = error as? MapError { exception = NSException(name: .init(rawValue: "MapError"), reason: mapError.description, userInfo: nil) } else { exception = NSException(name: .init(rawValue: "ImmutableMappableError"), reason: error.localizedDescription, userInfo: nil) } exception.raise() #else NSLog("\(error)") #endif } } else { // Ensure BaseMappable is not implemented directly assert(false, "BaseMappable should not be implemented directly. Please implement Mappable, StaticMappable or ImmutableMappable") } return nil } // MARK: Mapping functions for Arrays and Dictionaries /// Maps a JSON array to an object that conforms to Mappable public func mapArray(JSONString: String) -> [N]? { let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) if let objectArray = mapArray(JSONObject: parsedJSON) { return objectArray } // failed to parse JSON into array form // try to parse it into a dictionary and then wrap it in an array if let object = map(JSONObject: parsedJSON) { return [object] } return nil } /// Maps a JSON object to an array of Mappable objects if it is an array of JSON dictionary, or returns nil. public func mapArray(JSONObject: Any?) -> [N]? { if let JSONArray = JSONObject as? [[String: Any]] { return mapArray(JSONArray: JSONArray) } return nil } /// Maps an array of JSON dictionary to an array of Mappable objects public func mapArray(JSONArray: [[String: Any]]) -> [N] { // map every element in JSON array to type N let result = JSONArray.flatMap(map) return result } /// Maps a JSON object to a dictionary of Mappable objects if it is a JSON dictionary of dictionaries, or returns nil. public func mapDictionary(JSONString: String) -> [String: N]? { let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) return mapDictionary(JSONObject: parsedJSON) } /// Maps a JSON object to a dictionary of Mappable objects if it is a JSON dictionary of dictionaries, or returns nil. public func mapDictionary(JSONObject: Any?) -> [String: N]? { if let JSON = JSONObject as? [String: [String: Any]] { return mapDictionary(JSON: JSON) } return nil } /// Maps a JSON dictionary of dictionaries to a dictionary of Mappable objects public func mapDictionary(JSON: [String: [String: Any]]) -> [String: N]? { // map every value in dictionary to type N let result = JSON.filterMap(map) if result.isEmpty == false { return result } return nil } /// Maps a JSON object to a dictionary of Mappable objects if it is a JSON dictionary of dictionaries, or returns nil. public func mapDictionary(JSONObject: Any?, toDictionary dictionary: [String: N]) -> [String: N] { if let JSON = JSONObject as? [String : [String : Any]] { return mapDictionary(JSON: JSON, toDictionary: dictionary) } return dictionary } /// Maps a JSON dictionary of dictionaries to an existing dictionary of Mappable objects public func mapDictionary(JSON: [String: [String: Any]], toDictionary dictionary: [String: N]) -> [String: N] { var mutableDictionary = dictionary for (key, value) in JSON { if let object = dictionary[key] { _ = map(JSON: value, toObject: object) } else { mutableDictionary[key] = map(JSON: value) } } return mutableDictionary } /// Maps a JSON object to a dictionary of arrays of Mappable objects public func mapDictionaryOfArrays(JSONObject: Any?) -> [String: [N]]? { if let JSON = JSONObject as? [String: [[String: Any]]] { return mapDictionaryOfArrays(JSON: JSON) } return nil } ///Maps a JSON dictionary of arrays to a dictionary of arrays of Mappable objects public func mapDictionaryOfArrays(JSON: [String: [[String: Any]]]) -> [String: [N]]? { // map every value in dictionary to type N let result = JSON.filterMap { mapArray(JSONArray: $0) } if result.isEmpty == false { return result } return nil } /// Maps an 2 dimentional array of JSON dictionaries to a 2 dimentional array of Mappable objects public func mapArrayOfArrays(JSONObject: Any?) -> [[N]]? { if let JSONArray = JSONObject as? [[[String: Any]]] { var objectArray = [[N]]() for innerJSONArray in JSONArray { let array = mapArray(JSONArray: innerJSONArray) objectArray.append(array) } if objectArray.isEmpty == false { return objectArray } } return nil } // MARK: Utility functions for converting strings to JSON objects /// Convert a JSON String into a Dictionary using NSJSONSerialization public static func parseJSONStringIntoDictionary(JSONString: String) -> [String: Any]? { let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) return parsedJSON as? [String: Any] } /// Convert a JSON String into an Object using NSJSONSerialization public static func parseJSONString(JSONString: String) -> Any? { let data = JSONString.data(using: String.Encoding.utf8, allowLossyConversion: true) if let data = data { let parsedJSON: Any? do { parsedJSON = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) } catch let error { print(error) parsedJSON = nil } return parsedJSON } return nil } } extension Mapper { // MARK: Functions that create model from JSON file /// JSON file to Mappable object /// - parameter JSONfile: Filename /// - Returns: Mappable object public func map(JSONfile: String) -> N? { if let path = Bundle.main.path(forResource: JSONfile, ofType: nil) { do { let JSONString = try String(contentsOfFile: path) do { return self.map(JSONString: JSONString) } } catch { return nil } } return nil } /// JSON file to Mappable object array /// - parameter JSONfile: Filename /// - Returns: Mappable object array public func mapArray(JSONfile: String) -> [N]? { if let path = Bundle.main.path(forResource: JSONfile, ofType: nil) { do { let JSONString = try String(contentsOfFile: path) do { return self.mapArray(JSONString: JSONString) } } catch { return nil } } return nil } } extension Mapper { // MARK: Functions that create JSON from objects ///Maps an object that conforms to Mappable to a JSON dictionary public func toJSON(_ object: N) -> [String: Any] { var mutableObject = object let map = Map(mappingType: .toJSON, JSON: [:], context: context, shouldIncludeNilValues: shouldIncludeNilValues) mutableObject.mapping(map: map) return map.JSON } ///Maps an array of Objects to an array of JSON dictionaries [[String: Any]] public func toJSONArray(_ array: [N]) -> [[String: Any]] { return array.map { // convert every element in array to JSON dictionary equivalent self.toJSON($0) } } ///Maps a dictionary of Objects that conform to Mappable to a JSON dictionary of dictionaries. public func toJSONDictionary(_ dictionary: [String: N]) -> [String: [String: Any]] { return dictionary.map { (arg: (key: String, value: N)) in // convert every value in dictionary to its JSON dictionary equivalent return (arg.key, self.toJSON(arg.value)) } } ///Maps a dictionary of Objects that conform to Mappable to a JSON dictionary of dictionaries. public func toJSONDictionaryOfArrays(_ dictionary: [String: [N]]) -> [String: [[String: Any]]] { return dictionary.map { (arg: (key: String, value: [N])) in // convert every value (array) in dictionary to its JSON dictionary equivalent return (arg.key, self.toJSONArray(arg.value)) } } /// Maps an Object to a JSON string with option of pretty formatting public func toJSONString(_ object: N, prettyPrint: Bool = false) -> String? { let JSONDict = toJSON(object) return Mapper.toJSONString(JSONDict as Any, prettyPrint: prettyPrint) } /// Maps an array of Objects to a JSON string with option of pretty formatting public func toJSONString(_ array: [N], prettyPrint: Bool = false) -> String? { let JSONDict = toJSONArray(array) return Mapper.toJSONString(JSONDict as Any, prettyPrint: prettyPrint) } /// Converts an Object to a JSON string with option of pretty formatting public static func toJSONString(_ JSONObject: Any, prettyPrint: Bool) -> String? { let options: JSONSerialization.WritingOptions = prettyPrint ? .prettyPrinted : [] if let JSON = Mapper.toJSONData(JSONObject, options: options) { return String(data: JSON, encoding: String.Encoding.utf8) } return nil } /// Converts an Object to JSON data with options public static func toJSONData(_ JSONObject: Any, options: JSONSerialization.WritingOptions) -> Data? { if JSONSerialization.isValidJSONObject(JSONObject) { let JSONData: Data? do { JSONData = try JSONSerialization.data(withJSONObject: JSONObject, options: options) } catch let error { print(error) JSONData = nil } return JSONData } return nil } } extension Mapper where N: Hashable { /// Maps a JSON array to an object that conforms to Mappable public func mapSet(JSONString: String) -> Set? { let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) if let objectArray = mapArray(JSONObject: parsedJSON) { return Set(objectArray) } // failed to parse JSON into array form // try to parse it into a dictionary and then wrap it in an array if let object = map(JSONObject: parsedJSON) { return Set([object]) } return nil } /// Maps a JSON object to an Set of Mappable objects if it is an array of JSON dictionary, or returns nil. public func mapSet(JSONObject: Any?) -> Set? { if let JSONArray = JSONObject as? [[String: Any]] { return mapSet(JSONArray: JSONArray) } return nil } /// Maps an Set of JSON dictionary to an array of Mappable objects public func mapSet(JSONArray: [[String: Any]]) -> Set { // map every element in JSON array to type N return Set(JSONArray.flatMap(map)) } ///Maps a Set of Objects to a Set of JSON dictionaries [[String : Any]] public func toJSONSet(_ set: Set) -> [[String: Any]] { return set.map { // convert every element in set to JSON dictionary equivalent self.toJSON($0) } } /// Maps a set of Objects to a JSON string with option of pretty formatting public func toJSONString(_ set: Set, prettyPrint: Bool = false) -> String? { let JSONDict = toJSONSet(set) return Mapper.toJSONString(JSONDict as Any, prettyPrint: prettyPrint) } } extension Dictionary { internal func map(_ f: (Element) throws -> (K, V)) rethrows -> [K: V] { var mapped = [K: V]() for element in self { let newElement = try f(element) mapped[newElement.0] = newElement.1 } return mapped } internal func map(_ f: (Element) throws -> (K, [V])) rethrows -> [K: [V]] { var mapped = [K: [V]]() for element in self { let newElement = try f(element) mapped[newElement.0] = newElement.1 } return mapped } internal func filterMap(_ f: (Value) throws -> U?) rethrows -> [Key: U] { var mapped = [Key: U]() for (key, value) in self { if let newValue = try f(value) { mapped[key] = newValue } } return mapped } } ================================================ FILE: Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift ================================================ // // TransformOf.swift // ObjectMapper // // Created by Tristan Himmelman on 8/22/16. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation open class NSDecimalNumberTransform: TransformType { public typealias Object = NSDecimalNumber public typealias JSON = String public init() {} open func transformFromJSON(_ value: Any?) -> NSDecimalNumber? { if let string = value as? String { return NSDecimalNumber(string: string) } else if let number = value as? NSNumber { return NSDecimalNumber(decimal: number.decimalValue) } else if let double = value as? Double { return NSDecimalNumber(floatLiteral: double) } return nil } open func transformToJSON(_ value: NSDecimalNumber?) -> String? { guard let value = value else { return nil } return value.description } } ================================================ FILE: Pods/ObjectMapper/Sources/Operators.swift ================================================ // // Operators.swift // ObjectMapper // // Created by Tristan Himmelman on 2014-10-09. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. /** * This file defines a new operator which is used to create a mapping between an object and a JSON key value. * There is an overloaded operator definition for each type of object that is supported in ObjectMapper. * This provides a way to add custom logic to handle specific types of objects */ /// Operator used for defining mappings to and from JSON infix operator <- /// Operator used to define mappings to JSON infix operator >>> // MARK:- Objects with Basic types /// Object of Basic type public func <- (left: inout T, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.basicType(&left, object: right.value()) case .toJSON: left >>> right default: () } } public func >>> (left: T, right: Map) { if right.mappingType == .toJSON { ToJSON.basicType(left, map: right) } } /// Optional object of basic type public func <- (left: inout T?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalBasicType(&left, object: right.value()) case .toJSON: left >>> right default: () } } public func >>> (left: T?, right: Map) { if right.mappingType == .toJSON { ToJSON.optionalBasicType(left, map: right) } } /// Implicitly unwrapped optional object of basic type public func <- (left: inout T!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalBasicType(&left, object: right.value()) case .toJSON: left >>> right default: () } } // MARK:- Mappable Objects - /// Object conforming to Mappable public func <- (left: inout T, right: Map) { switch right.mappingType { case .fromJSON: FromJSON.object(&left, map: right) case .toJSON: left >>> right } } public func >>> (left: T, right: Map) { if right.mappingType == .toJSON { ToJSON.object(left, map: right) } } /// Optional Mappable objects public func <- (left: inout T?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObject(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: T?, right: Map) { if right.mappingType == .toJSON { ToJSON.optionalObject(left, map: right) } } /// Implicitly unwrapped optional Mappable objects public func <- (left: inout T!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObject(&left, map: right) case .toJSON: left >>> right default: () } } // MARK:- Dictionary of Mappable objects - Dictionary /// Dictionary of Mappable objects public func <- (left: inout Dictionary, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.objectDictionary(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Dictionary, right: Map) { if right.mappingType == .toJSON { ToJSON.objectDictionary(left, map: right) } } /// Optional Dictionary of Mappable object public func <- (left: inout Dictionary?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectDictionary(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Dictionary?, right: Map) { if right.mappingType == .toJSON { ToJSON.optionalObjectDictionary(left, map: right) } } /// Implicitly unwrapped Optional Dictionary of Mappable object public func <- (left: inout Dictionary!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectDictionary(&left, map: right) case .toJSON: left >>> right default: () } } /// Dictionary of Mappable objects public func <- (left: inout Dictionary, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.objectDictionaryOfArrays(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Dictionary, right: Map) { if right.mappingType == .toJSON { ToJSON.objectDictionaryOfArrays(left, map: right) } } /// Optional Dictionary of Mappable object public func <- (left: inout Dictionary?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectDictionaryOfArrays(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Dictionary?, right: Map) { if right.mappingType == .toJSON { ToJSON.optionalObjectDictionaryOfArrays(left, map: right) } } /// Implicitly unwrapped Optional Dictionary of Mappable object public func <- (left: inout Dictionary!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectDictionaryOfArrays(&left, map: right) case .toJSON: left >>> right default: () } } // MARK:- Array of Mappable objects - Array /// Array of Mappable objects public func <- (left: inout Array, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.objectArray(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Array, right: Map) { if right.mappingType == .toJSON { ToJSON.objectArray(left, map: right) } } /// Optional array of Mappable objects public func <- (left: inout Array?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectArray(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Array?, right: Map) { if right.mappingType == .toJSON { ToJSON.optionalObjectArray(left, map: right) } } /// Implicitly unwrapped Optional array of Mappable objects public func <- (left: inout Array!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectArray(&left, map: right) case .toJSON: left >>> right default: () } } // MARK:- Array of Array of Mappable objects - Array> /// Array of Array Mappable objects public func <- (left: inout Array>, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.twoDimensionalObjectArray(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Array>, right: Map) { if right.mappingType == .toJSON { ToJSON.twoDimensionalObjectArray(left, map: right) } } /// Optional array of Mappable objects public func <- (left:inout Array>?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalTwoDimensionalObjectArray(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Array>?, right: Map) { if right.mappingType == .toJSON { ToJSON.optionalTwoDimensionalObjectArray(left, map: right) } } /// Implicitly unwrapped Optional array of Mappable objects public func <- (left: inout Array>!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalTwoDimensionalObjectArray(&left, map: right) case .toJSON: left >>> right default: () } } // MARK:- Set of Mappable objects - Set /// Set of Mappable objects public func <- (left: inout Set, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.objectSet(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Set, right: Map) { if right.mappingType == .toJSON { ToJSON.objectSet(left, map: right) } } /// Optional Set of Mappable objects public func <- (left: inout Set?, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectSet(&left, map: right) case .toJSON: left >>> right default: () } } public func >>> (left: Set?, right: Map) { if right.mappingType == .toJSON { ToJSON.optionalObjectSet(left, map: right) } } /// Implicitly unwrapped Optional Set of Mappable objects public func <- (left: inout Set!, right: Map) { switch right.mappingType { case .fromJSON where right.isKeyPresent: FromJSON.optionalObjectSet(&left, map: right) case .toJSON: left >>> right default: () } } ================================================ FILE: Pods/ObjectMapper/Sources/ToJSON.swift ================================================ // // ToJSON.swift // ObjectMapper // // Created by Tristan Himmelman on 2014-10-13. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation private func setValue(_ value: Any, map: Map) { setValue(value, key: map.currentKey!, checkForNestedKeys: map.keyIsNested, delimiter: map.nestedKeyDelimiter, dictionary: &map.JSON) } private func setValue(_ value: Any, key: String, checkForNestedKeys: Bool, delimiter: String, dictionary: inout [String : Any]) { if checkForNestedKeys { let keyComponents = ArraySlice(key.components(separatedBy: delimiter).filter { !$0.isEmpty }.map { $0 }) setValue(value, forKeyPathComponents: keyComponents, dictionary: &dictionary) } else { dictionary[key] = value } } private func setValue(_ value: Any, forKeyPathComponents components: ArraySlice, dictionary: inout [String : Any]) { if components.isEmpty { return } let head = components.first! if components.count == 1 { dictionary[String(head)] = value } else { var child = dictionary[String(head)] as? [String : Any] if child == nil { child = [:] } let tail = components.dropFirst() setValue(value, forKeyPathComponents: tail, dictionary: &child!) dictionary[String(head)] = child } } internal final class ToJSON { class func basicType(_ field: N, map: Map) { if let x = field as Any? , false || x is NSNumber // Basic types || x is Bool || x is Int || x is Double || x is Float || x is String || x is NSNull || x is Array // Arrays || x is Array || x is Array || x is Array || x is Array || x is Array || x is Array || x is Array> || x is Dictionary // Dictionaries || x is Dictionary || x is Dictionary || x is Dictionary || x is Dictionary || x is Dictionary || x is Dictionary { setValue(x, map: map) } } class func optionalBasicType(_ field: N?, map: Map) { if let field = field { basicType(field, map: map) } else if map.shouldIncludeNilValues { basicType(NSNull(), map: map) //If BasicType is nil, emil NSNull into the JSON output } } class func object(_ field: N, map: Map) { if let result = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSON(field) as Any? { setValue(result, map: map) } } class func optionalObject(_ field: N?, map: Map) { if let field = field { object(field, map: map) } } class func objectArray(_ field: Array, map: Map) { let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONArray(field) setValue(JSONObjects, map: map) } class func optionalObjectArray(_ field: Array?, map: Map) { if let field = field { objectArray(field, map: map) } } class func twoDimensionalObjectArray(_ field: Array>, map: Map) { var array = [[[String: Any]]]() for innerArray in field { let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONArray(innerArray) array.append(JSONObjects) } setValue(array, map: map) } class func optionalTwoDimensionalObjectArray(_ field: Array>?, map: Map) { if let field = field { twoDimensionalObjectArray(field, map: map) } } class func objectSet(_ field: Set, map: Map) { let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONSet(field) setValue(JSONObjects, map: map) } class func optionalObjectSet(_ field: Set?, map: Map) { if let field = field { objectSet(field, map: map) } } class func objectDictionary(_ field: Dictionary, map: Map) { let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONDictionary(field) setValue(JSONObjects, map: map) } class func optionalObjectDictionary(_ field: Dictionary?, map: Map) { if let field = field { objectDictionary(field, map: map) } } class func objectDictionaryOfArrays(_ field: Dictionary, map: Map) { let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONDictionaryOfArrays(field) setValue(JSONObjects, map: map) } class func optionalObjectDictionaryOfArrays(_ field: Dictionary?, map: Map) { if let field = field { objectDictionaryOfArrays(field, map: map) } } } ================================================ FILE: Pods/ObjectMapper/Sources/TransformOf.swift ================================================ // // TransformOf.swift // ObjectMapper // // Created by Syo Ikeda on 1/23/15. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. open class TransformOf: TransformType { public typealias Object = ObjectType public typealias JSON = JSONType private let fromJSON: (JSONType?) -> ObjectType? private let toJSON: (ObjectType?) -> JSONType? public init(fromJSON: @escaping(JSONType?) -> ObjectType?, toJSON: @escaping(ObjectType?) -> JSONType?) { self.fromJSON = fromJSON self.toJSON = toJSON } open func transformFromJSON(_ value: Any?) -> ObjectType? { return fromJSON(value as? JSONType) } open func transformToJSON(_ value: ObjectType?) -> JSONType? { return toJSON(value) } } ================================================ FILE: Pods/ObjectMapper/Sources/TransformOperators.swift ================================================ // // TransformOperators.swift // ObjectMapper // // Created by Tristan Himmelman on 2016-09-26. // Copyright © 2016 hearst. All rights reserved. // import Foundation // MARK:- Transforms /// Object of Basic type with Transform public func <- (left: inout Transform.Object, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let value = transform.transformFromJSON(map.currentValue) FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } public func >>> (left: Transform.Object, right: (Map, Transform)) { let (map, transform) = right if map.mappingType == .toJSON { let value: Transform.JSON? = transform.transformToJSON(left) ToJSON.optionalBasicType(value, map: map) } } /// Optional object of basic type with Transform public func <- (left: inout Transform.Object?, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let value = transform.transformFromJSON(map.currentValue) FromJSON.optionalBasicType(&left, object: value) case .toJSON: left >>> right default: () } } public func >>> (left: Transform.Object?, right: (Map, Transform)) { let (map, transform) = right if map.mappingType == .toJSON { let value: Transform.JSON? = transform.transformToJSON(left) ToJSON.optionalBasicType(value, map: map) } } /// Implicitly unwrapped optional object of basic type with Transform public func <- (left: inout Transform.Object!, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let value = transform.transformFromJSON(map.currentValue) FromJSON.optionalBasicType(&left, object: value) case .toJSON: left >>> right default: () } } /// Array of Basic type with Transform public func <- (left: inout [Transform.Object], right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let values = fromJSONArrayWithTransform(map.currentValue, transform: transform) FromJSON.basicType(&left, object: values) case .toJSON: left >>> right default: () } } public func >>> (left: [Transform.Object], right: (Map, Transform)) { let (map, transform) = right if map.mappingType == .toJSON{ let values = toJSONArrayWithTransform(left, transform: transform) ToJSON.optionalBasicType(values, map: map) } } /// Optional array of Basic type with Transform public func <- (left: inout [Transform.Object]?, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let values = fromJSONArrayWithTransform(map.currentValue, transform: transform) FromJSON.optionalBasicType(&left, object: values) case .toJSON: left >>> right default: () } } public func >>> (left: [Transform.Object]?, right: (Map, Transform)) { let (map, transform) = right if map.mappingType == .toJSON { let values = toJSONArrayWithTransform(left, transform: transform) ToJSON.optionalBasicType(values, map: map) } } /// Implicitly unwrapped optional array of Basic type with Transform public func <- (left: inout [Transform.Object]!, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let values = fromJSONArrayWithTransform(map.currentValue, transform: transform) FromJSON.optionalBasicType(&left, object: values) case .toJSON: left >>> right default: () } } /// Dictionary of Basic type with Transform public func <- (left: inout [String: Transform.Object], right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let values = fromJSONDictionaryWithTransform(map.currentValue, transform: transform) FromJSON.basicType(&left, object: values) case .toJSON: left >>> right default: () } } public func >>> (left: [String: Transform.Object], right: (Map, Transform)) { let (map, transform) = right if map.mappingType == . toJSON { let values = toJSONDictionaryWithTransform(left, transform: transform) ToJSON.optionalBasicType(values, map: map) } } /// Optional dictionary of Basic type with Transform public func <- (left: inout [String: Transform.Object]?, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let values = fromJSONDictionaryWithTransform(map.currentValue, transform: transform) FromJSON.optionalBasicType(&left, object: values) case .toJSON: left >>> right default: () } } public func >>> (left: [String: Transform.Object]?, right: (Map, Transform)) { let (map, transform) = right if map.mappingType == .toJSON { let values = toJSONDictionaryWithTransform(left, transform: transform) ToJSON.optionalBasicType(values, map: map) } } /// Implicitly unwrapped optional dictionary of Basic type with Transform public func <- (left: inout [String: Transform.Object]!, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let values = fromJSONDictionaryWithTransform(map.currentValue, transform: transform) FromJSON.optionalBasicType(&left, object: values) case .toJSON: left >>> right default: () } } // MARK:- Transforms of Mappable Objects - /// Object conforming to Mappable that have transforms public func <- (left: inout Transform.Object, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let value: Transform.Object? = transform.transformFromJSON(map.currentValue) FromJSON.basicType(&left, object: value) case .toJSON: left >>> right default: () } } public func >>> (left: Transform.Object, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let value: Transform.JSON? = transform.transformToJSON(left) ToJSON.optionalBasicType(value, map: map) } } /// Optional Mappable objects that have transforms public func <- (left: inout Transform.Object?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let value: Transform.Object? = transform.transformFromJSON(map.currentValue) FromJSON.optionalBasicType(&left, object: value) case .toJSON: left >>> right default: () } } public func >>> (left: Transform.Object?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON{ let value: Transform.JSON? = transform.transformToJSON(left) ToJSON.optionalBasicType(value, map: map) } } /// Implicitly unwrapped optional Mappable objects that have transforms public func <- (left: inout Transform.Object!, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let value: Transform.Object? = transform.transformFromJSON(map.currentValue) FromJSON.optionalBasicType(&left, object: value) case .toJSON: left >>> right default: () } } // MARK:- Dictionary of Mappable objects with a transform - Dictionary /// Dictionary of Mappable objects with a transform public func <- (left: inout Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .fromJSON && map.isKeyPresent, let object = map.currentValue as? [String: Any] { let value = fromJSONDictionaryWithTransform(object as Any?, transform: transform) ?? left FromJSON.basicType(&left, object: value) } else if map.mappingType == .toJSON { left >>> right } } public func >>> (left: Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let value = toJSONDictionaryWithTransform(left, transform: transform) ToJSON.basicType(value, map: map) } } /// Optional Dictionary of Mappable object with a transform public func <- (left: inout Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .fromJSON && map.isKeyPresent, let object = map.currentValue as? [String : Any]{ let value = fromJSONDictionaryWithTransform(object as Any?, transform: transform) ?? left FromJSON.optionalBasicType(&left, object: value) } else if map.mappingType == .toJSON { left >>> right } } public func >>> (left: Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let value = toJSONDictionaryWithTransform(left, transform: transform) ToJSON.optionalBasicType(value, map: map) } } /// Implicitly unwrapped Optional Dictionary of Mappable object with a transform public func <- (left: inout Dictionary!, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .fromJSON && map.isKeyPresent, let dictionary = map.currentValue as? [String : Any]{ let transformedDictionary = fromJSONDictionaryWithTransform(dictionary as Any?, transform: transform) ?? left FromJSON.optionalBasicType(&left, object: transformedDictionary) } else if map.mappingType == .toJSON { left >>> right } } /// Dictionary of Mappable objects with a transform public func <- (left: inout Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if let dictionary = map.currentValue as? [String : [Any]], map.mappingType == .fromJSON && map.isKeyPresent { let transformedDictionary = dictionary.map { (arg: (key: String, values: [Any])) -> (String, [Transform.Object]) in let (key, values) = arg if let jsonArray = fromJSONArrayWithTransform(values, transform: transform) { return (key, jsonArray) } if let leftValue = left[key] { return (key, leftValue) } return (key, []) } FromJSON.basicType(&left, object: transformedDictionary) } else if map.mappingType == .toJSON { left >>> right } } public func >>> (left: Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let transformedDictionary = left.map { (arg: (key: String, value: [Transform.Object])) in return (arg.key, toJSONArrayWithTransform(arg.value, transform: transform) ?? []) } ToJSON.basicType(transformedDictionary, map: map) } } /// Optional Dictionary of Mappable object with a transform public func <- (left: inout Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if let dictionary = map.currentValue as? [String : [Any]], map.mappingType == .fromJSON && map.isKeyPresent { let transformedDictionary = dictionary.map { (arg: (key: String, values: [Any])) -> (String, [Transform.Object]) in let (key, values) = arg if let jsonArray = fromJSONArrayWithTransform(values, transform: transform) { return (key, jsonArray) } if let leftValue = left?[key] { return (key, leftValue) } return (key, []) } FromJSON.optionalBasicType(&left, object: transformedDictionary) } else if map.mappingType == .toJSON { left >>> right } } public func >>> (left: Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let transformedDictionary = left?.map { (arg: (key: String, values: [Transform.Object])) in return (arg.key, toJSONArrayWithTransform(arg.values, transform: transform) ?? []) } ToJSON.optionalBasicType(transformedDictionary, map: map) } } /// Implicitly unwrapped Optional Dictionary of Mappable object with a transform public func <- (left: inout Dictionary!, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if let dictionary = map.currentValue as? [String : [Any]], map.mappingType == .fromJSON && map.isKeyPresent { let transformedDictionary = dictionary.map { (arg: (key: String, values: [Any])) -> (String, [Transform.Object]) in let (key, values) = arg if let jsonArray = fromJSONArrayWithTransform(values, transform: transform) { return (key, jsonArray) } if let leftValue = left?[key] { return (key, leftValue) } return (key, []) } FromJSON.optionalBasicType(&left, object: transformedDictionary) } else if map.mappingType == .toJSON { left >>> right } } // MARK:- Array of Mappable objects with transforms - Array /// Array of Mappable objects public func <- (left: inout Array, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { FromJSON.basicType(&left, object: transformedValues) } case .toJSON: left >>> right default: () } } public func >>> (left: Array, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let transformedValues = toJSONArrayWithTransform(left, transform: transform) ToJSON.optionalBasicType(transformedValues, map: map) } } /// Optional array of Mappable objects public func <- (left: inout Array?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) FromJSON.optionalBasicType(&left, object: transformedValues) case .toJSON: left >>> right default: () } } public func >>> (left: Array?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let transformedValues = toJSONArrayWithTransform(left, transform: transform) ToJSON.optionalBasicType(transformedValues, map: map) } } /// Implicitly unwrapped Optional array of Mappable objects public func <- (left: inout Array!, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) FromJSON.optionalBasicType(&left, object: transformedValues) case .toJSON: left >>> right default: () } } // MARK:- Array of Array of objects - Array>> with transforms /// Array of Array of objects with transform public func <- (left: inout [[Transform.Object]], right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .toJSON: left >>> right case .fromJSON where map.isKeyPresent: guard let original2DArray = map.currentValue as? [[Any]] else { break } let transformed2DArray = original2DArray.flatMap { values in fromJSONArrayWithTransform(values as Any?, transform: transform) } FromJSON.basicType(&left, object: transformed2DArray) default: break } } public func >>> (left: [[Transform.Object]], right: (Map, Transform)) { let (map, transform) = right if map.mappingType == .toJSON{ let transformed2DArray = left.flatMap { values in toJSONArrayWithTransform(values, transform: transform) } ToJSON.basicType(transformed2DArray, map: map) } } /// Optional array of array of objects with transform public func <- (left: inout [[Transform.Object]]?, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .toJSON: left >>> right case .fromJSON where map.isKeyPresent: guard let original2DArray = map.currentValue as? [[Any]] else { break } let transformed2DArray = original2DArray.flatMap { values in fromJSONArrayWithTransform(values as Any?, transform: transform) } FromJSON.optionalBasicType(&left, object: transformed2DArray) default: break } } public func >>> (left: [[Transform.Object]]?, right: (Map, Transform)) { let (map, transform) = right if map.mappingType == .toJSON { let transformed2DArray = left?.flatMap { values in toJSONArrayWithTransform(values, transform: transform) } ToJSON.optionalBasicType(transformed2DArray, map: map) } } /// Implicitly unwrapped Optional array of array of objects with transform public func <- (left: inout [[Transform.Object]]!, right: (Map, Transform)) { let (map, transform) = right switch map.mappingType { case .toJSON: left >>> right case .fromJSON where map.isKeyPresent: guard let original2DArray = map.currentValue as? [[Any]] else { break } let transformed2DArray = original2DArray.flatMap { values in fromJSONArrayWithTransform(values as Any?, transform: transform) } FromJSON.optionalBasicType(&left, object: transformed2DArray) default: break } } // MARK:- Set of Mappable objects with a transform - Set /// Set of Mappable objects with transform public func <- (left: inout Set, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { FromJSON.basicType(&left, object: Set(transformedValues)) } case .toJSON: left >>> right default: () } } public func >>> (left: Set, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { let transformedValues = toJSONArrayWithTransform(Array(left), transform: transform) ToJSON.optionalBasicType(transformedValues, map: map) } } /// Optional Set of Mappable objects with transform public func <- (left: inout Set?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { FromJSON.basicType(&left, object: Set(transformedValues)) } case .toJSON: left >>> right default: () } } public func >>> (left: Set?, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right if map.mappingType == .toJSON { if let values = left { let transformedValues = toJSONArrayWithTransform(Array(values), transform: transform) ToJSON.optionalBasicType(transformedValues, map: map) } } } /// Implicitly unwrapped Optional set of Mappable objects with transform public func <- (left: inout Set!, right: (Map, Transform)) where Transform.Object: BaseMappable { let (map, transform) = right switch map.mappingType { case .fromJSON where map.isKeyPresent: if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { FromJSON.basicType(&left, object: Set(transformedValues)) } case .toJSON: left >>> right default: () } } private func fromJSONArrayWithTransform(_ input: Any?, transform: Transform) -> [Transform.Object]? { if let values = input as? [Any] { return values.flatMap { value in return transform.transformFromJSON(value) } } else { return nil } } private func fromJSONDictionaryWithTransform(_ input: Any?, transform: Transform) -> [String: Transform.Object]? { if let values = input as? [String: Any] { return values.filterMap { value in return transform.transformFromJSON(value) } } else { return nil } } private func toJSONArrayWithTransform(_ input: [Transform.Object]?, transform: Transform) -> [Transform.JSON]? { return input?.flatMap { value in return transform.transformToJSON(value) } } private func toJSONDictionaryWithTransform(_ input: [String: Transform.Object]?, transform: Transform) -> [String: Transform.JSON]? { return input?.filterMap { value in return transform.transformToJSON(value) } } ================================================ FILE: Pods/ObjectMapper/Sources/TransformType.swift ================================================ // // TransformType.swift // ObjectMapper // // Created by Syo Ikeda on 2/4/15. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. public protocol TransformType { associatedtype Object associatedtype JSON func transformFromJSON(_ value: Any?) -> Object? func transformToJSON(_ value: Object?) -> JSON? } ================================================ FILE: Pods/ObjectMapper/Sources/URLTransform.swift ================================================ // // URLTransform.swift // ObjectMapper // // Created by Tristan Himmelman on 2014-10-27. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // 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. import Foundation open class URLTransform: TransformType { public typealias Object = URL public typealias JSON = String private let shouldEncodeURLString: Bool private let allowedCharacterSet: CharacterSet /** Initializes the URLTransform with an option to encode URL strings before converting them to an NSURL - parameter shouldEncodeUrlString: when true (the default) the string is encoded before passing to `NSURL(string:)` - returns: an initialized transformer */ public init(shouldEncodeURLString: Bool = false, allowedCharacterSet: CharacterSet = .urlQueryAllowed) { self.shouldEncodeURLString = shouldEncodeURLString self.allowedCharacterSet = allowedCharacterSet } open func transformFromJSON(_ value: Any?) -> URL? { guard let URLString = value as? String else { return nil } if !shouldEncodeURLString { return URL(string: URLString) } guard let escapedURLString = URLString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) else { return nil } return URL(string: escapedURLString) } open func transformToJSON(_ value: URL?) -> String? { if let URL = value { return URL.absoluteString } return nil } } ================================================ FILE: Pods/Pods.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 017B5B39D7D0CCD88DEAB2F1F5D0D274 /* TYPageControl.h in Headers */ = {isa = PBXBuildFile; fileRef = A93A51F7564EC64C94864C9A11DF7A0D /* TYPageControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; 019D5A551A09E129AE3D95BCB378028E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 022580D7BA3860D083FC817324DDD52D /* AsMaybe.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA69717822ACCAAF367E9F539039219 /* AsMaybe.swift */; }; 023B9DEF425CEF2B0993CC116B9D7748 /* Completable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52986B63E04FBCE90E31182FC836DF8C /* Completable.swift */; }; 02A8CD406255AE081E99A62241BAC587 /* IQToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155A351F30BA1128E99E067F81CB72B2 /* IQToolbar.swift */; }; 03649B1E46C35FC5D9BC8CE4EFC3AF5D /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */; }; 03905D0FD58E772457B809C9443B9633 /* NetworkActivityPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C02566F282B7374FFE638BE27D65EBC /* NetworkActivityPlugin.swift */; }; 03CE3310204ECD17C80FA496A818DA47 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DBD4CA78A84FE60982AF0780EC62DD2 /* SwiftyJSON.swift */; }; 03E8239071E8786ED0AFD90253814966 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C099BC759B6B4145E48DE537580E13 /* Throttle.swift */; }; 051F11606D79C85C63FD6BDEE5C2A6E8 /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C5BD7D77FAF305B2C6E8F85C768FC42 /* RefCountDisposable.swift */; }; 05E3461942D63CA142876829DF2178B3 /* DispatchQueue+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5367CAC7283271F0E12047EE1E54190 /* DispatchQueue+Extensions.swift */; }; 0638F162B79029D25571C968F00A8721 /* InfiniteSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404B3E0121644CBA834D592A1EB4E364 /* InfiniteSequence.swift */; }; 068C7761D808018F145A97CA20A231BC /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = 096040CC61B2863666482591D451AA38 /* Just.swift */; }; 068EE77B364B309A5973AA63DCE763D0 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A0AC039E269E338928C0D25F4ABC01 /* Endpoint.swift */; }; 06CCF3314F269406C8705B30D86CFD7C /* TextInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE48FA13FBA4585E33723ED2572A6754 /* TextInput.swift */; }; 07159A933B3C47A02FE2B2744ABAAC89 /* MJRefreshStateHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 613B23B6CFFCB99006B5893BE2C653A5 /* MJRefreshStateHeader.m */; }; 07432E8DEA8B25795C7C545AA67A0187 /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FC32B26F57769DE2EFE454C78CC62C /* ImageTransition.swift */; }; 07786426A59FC42A5077B8B28EF25834 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF8C44E75B472322D8DC2B86B893ED9 /* SkipWhile.swift */; }; 07CBA63D00073C1C74402986AE9277C3 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039485063DF511DC852BBCD65C474C9B /* Timer.swift */; }; 084482D74A1740BF8258EEF9FEC9C93A /* Maybe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C8ADE73FE10FA15ABAA1CB4FB6DAD62 /* Maybe.swift */; }; 089250578AFBD4AD861843847252D462 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 09FA411E4CCB0B52628890F610065A66 /* DelaySubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770CC8138D39A339E5C8057EC2DD1F7D /* DelaySubscription.swift */; }; 0A10103215004C9822201FB9572B2C00 /* TYPagerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F5B9A2F08507775E806FE12ED90BAF6 /* TYPagerController.m */; }; 0A1E34B4A27DCD62A914F3B028A07E6C /* UIView+MJExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = ABF289602106E94D3F277B9C7F39FF7C /* UIView+MJExtension.m */; }; 0A936243CBB929114D3405F0AC7C234F /* UIRefreshControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7645EEB7C195C4CF487DFA28BA57D8E5 /* UIRefreshControl+Rx.swift */; }; 0A9C9898D2B1246BFC5F26FC2E44F312 /* URLConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C85F1DBF1BC0BBAE450D1FBEA54A9F /* URLConvertible.swift */; }; 0C83C395557B207600B4BFEFB71F662D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 0CA6357476373C221D44C498AA1D65FE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 0DC7585DE4582FFC698D5CA7F7987868 /* ConstraintMultiplierTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E2178443AA8FE2901D19303B3B8F9 /* ConstraintMultiplierTarget.swift */; }; 0DD634DCA2EE6C8DA4C13EEC73849EFD /* ConstraintAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EB9FACBAF778C2D7E39429AAFFADE7 /* ConstraintAttributes.swift */; }; 0DF6F077A01F83D2296F474A6657D3DA /* UICollectionView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BA22372DFDD297D4C486B391AE3C50 /* UICollectionView+Rx.swift */; }; 0EEB4473FC528447BEC20C32448BF58E /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 593B68AD069BBE7957F7B4AE1E9F164A /* InvocableType.swift */; }; 0EEBFA3280A544CB4F48B94672825C03 /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A0824858ABB06E40B1588D14FF9498 /* Constraint.swift */; }; 0EFB524EFDF2F8712A2F5067817A672E /* UIScrollView+MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = F76E37DE05B72E5ACE3CC2FEA524DC00 /* UIScrollView+MJRefresh.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0F7E94DE4A01E0C2DE3D8FEF7A8DA76B /* NSTextStorage+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F728D985BB2068A70834B0E454690223 /* NSTextStorage+Rx.swift */; }; 0F981C56836FE34E921BC7253826A170 /* CustomDateFormatTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D811B382CEAC4FADDA971AFE90AD50A /* CustomDateFormatTransform.swift */; }; 0FD6CD7FBCB5EB6CEC1CABC3CAE78B12 /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14223DB3BAAFC3650B856F3ED4CA3293 /* Merge.swift */; }; 101F0992A8ADA071018179C8A35A2DB6 /* Completable+AndThen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF01C2ED8BF6578C8212FD3EAC90D3E2 /* Completable+AndThen.swift */; }; 101F836F887C01B45F4DCF351EBC28D1 /* MJRefreshBackNormalFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = DC01CD4F0AF25E87AE052DB77D446F12 /* MJRefreshBackNormalFooter.m */; }; 1091FE5A37CC5E22263AED304386D408 /* SwitchIfEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E83A69D9C8F0B40317DD90BF832D26 /* SwitchIfEmpty.swift */; }; 10AD5E70FDD53228153032C58FBF4D52 /* String+MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348D24C7C0138AD0B581B484EA58AD8B /* String+MD5.swift */; }; 10B28E1329EC509470D6073426296383 /* AnonymousDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B487DA905C4F895E0D126C1A062261 /* AnonymousDisposable.swift */; }; 10EB23E9ECC4B33E16933BB1EA560B6A /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF0A342E014C442F7C7474857AF5961 /* Timeline.swift */; }; 111388F3289E572B7FF90312D3C23DD1 /* RxTableViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7553CCC7443EE20DAEB595357CD7198F /* RxTableViewDataSourceProxy.swift */; }; 11F2729CD19FF2CBFE91E3D4A5A3D088 /* String+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D3CBDD7BD1351D05617EDFD0171017D /* String+Rx.swift */; }; 1248CDC903C16DF981D48B43CC8CFDC5 /* UINavigationItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 610EF28DB806E2D0ECBF17409EB9EABE /* UINavigationItem+Rx.swift */; }; 1314F3F62B92768E16CBFF1490AF29DE /* TYTabPagerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24B87BC6C657291DEE2644EFCB9CA341 /* TYTabPagerController.m */; }; 134859C42E05396F62489E4329ECF6EA /* ToJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42A26062E1F6F9B2A5BC508EA3D00288 /* ToJSON.swift */; }; 13575C4CA4D1D8F0DBB6E2164CBFF620 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB825CD3DE4C8C88F1EB6E75E3046B2 /* SwiftSupport.swift */; }; 1500C42EA85F1F32A0DFA87FE8320E24 /* SnapKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E257A033C34CB083326378E455CBA7C /* SnapKit-dummy.m */; }; 15597F37A6D45F2D0F52932109960222 /* SwiftyJSON-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C8B8D267B8E5E2A789EBA75CCFF121F2 /* SwiftyJSON-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 157E33FBE3B7D1D7996ADD2848628BC2 /* ConstraintDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB1A48DF38EB6B4A32ADD9DF79B73FF /* ConstraintDescription.swift */; }; 15A0DC01842AFC68403331D8324AC790 /* ConstraintRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C458E0D7CD6F0DD83AC852BC8419AD7 /* ConstraintRelation.swift */; }; 176FE089B6CA019DC6F5B0D2521FDFB7 /* TYTabPagerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 22B5011A39DF5849503132CEE075CC1E /* TYTabPagerView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1784A1AE7CB88EA9BF1A9CF7377C8090 /* Producer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DC2D7127D27BDD70C5EC0A4FF96074D /* Producer.swift */; }; 17F033046E658F3BFD0011DCB768CF54 /* MJRefreshBackNormalFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = F1D0EF5DC8B377434B4B9FAA777B65E0 /* MJRefreshBackNormalFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1827E7D94F3A12CA0C58EF33AE4B026D /* KingfisherOptionsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B28CBB9A2FC6BD76A7188D520046176D /* KingfisherOptionsInfo.swift */; }; 1843E6DF520A5EFE9C83C38E18578E42 /* MJRefreshAutoStateFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = E6F687744CD01C74158C6A8CA1C9B497 /* MJRefreshAutoStateFooter.m */; }; 186AC9E5D41A13F254AA6A111A659C86 /* DispatchQueue+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BC1A1EF9084AA9223F0E5E094C6ABC /* DispatchQueue+Extensions.swift */; }; 1894026C296B8A77711607C98ED40C86 /* IntegerOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B6FFA1A029CC85A9BD8A4847631CD7 /* IntegerOperators.swift */; }; 18C27FC3DA935FF02249B00D2410C6D3 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80A58779165736F60C355BBAAF75C027 /* Errors.swift */; }; 18E9B5E96557418567FCB7DCC559299F /* TransformOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03D2DC1D0CBA5B7FB557A21D0DA5771 /* TransformOperators.swift */; }; 194858D10B9B1DD9FFEFBE4EAA0700F3 /* TransformOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DED5486ED70B440763840FA459F4698 /* TransformOf.swift */; }; 1991BB121497AC9447A5DC6445464176 /* UITextField+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7088D6C1ADBF5BBD12CFF84A0E220FA2 /* UITextField+Rx.swift */; }; 1A84FB9D49D5E5E8EC11B4BFA93CD7FE /* Pods-RxXMLY-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 17CB2FA1178A4F2E225AE19BEFEB3B81 /* Pods-RxXMLY-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1B9EDEDC964E6B08F78920B4F4B9DB84 /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 21805A4E1E5079259A7A86A6D073E71B /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1C8AFF6985D76BB339539D41CCC403F6 /* Dematerialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACB6BCA51D81D396B134C596B0FB8C1 /* Dematerialize.swift */; }; 1D16749675E7E62321F1D550ED6CE78F /* SubscribeOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 137E979916631AD9ACADADEEEBBBAE5B /* SubscribeOn.swift */; }; 1DAF3BB88351664576507F774FEB051C /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D3E83D571BF2E77162C09F72FD66D3C /* Filter.swift */; }; 1E340BDD912879B4160C43D9982104EC /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D6508C06EC007A57DBB3099AD5C617 /* LockOwnerType.swift */; }; 1ED1D03FFEB9595492A4F8783D35F81C /* UIView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546B56E228E6A78E32833D60BB6D648 /* UIView+Rx.swift */; }; 1ED505CC385A149F4DF8F4B94BA4FC0B /* MJRefreshBackStateFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = F9218E907F18A770B1DFDE2EDEAA3480 /* MJRefreshBackStateFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1F0F49D029C4471F2AE3712D1FCB1D8A /* IQTitleBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F99F2A0C1CCF46EC645BA9E65F78200 /* IQTitleBarButtonItem.swift */; }; 1F242D91138AD1C8F49DCDA41B71461F /* UISearchBar+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2316CDD5367CCF5307F4C680E70DBBA /* UISearchBar+Rx.swift */; }; 1F34D539C0D864807B558FC80839C15A /* ConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10DC35A2E29CA2B46E24A2F022E9163B /* ConstraintItem.swift */; }; 1FCC0970C8435F10768CA84C2025A177 /* Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B95FDEC37C94B6EBD089F1AD6038F94 /* Resource.swift */; }; 2082EB4AA16440FFD5479FAFC0181066 /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D450DFC0A437B3075E6E68C0FA1A2B3 /* Indicator.swift */; }; 208482EE9B0AF0B0557F8E32F1B7A290 /* DispatchQueueConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36DC95F95B0777638C99DDB71789CF16 /* DispatchQueueConfiguration.swift */; }; 216DC034D3FF185097D5C162323EA37C /* SchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE49DD1F3CB80E0DCB3ED742E3DED62B /* SchedulerType.swift */; }; 22B40C80735B7CFAC7106C5FF6EA542F /* URLNavigator-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D783020743D1A18018EE614D78D965A2 /* URLNavigator-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2305F81424A8EA4AC22ED71E2EE69AD1 /* PriorityQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEB35F8259524796939172BEB9769CB6 /* PriorityQueue.swift */; }; 2384AC373E752D55842F37DE4F2CE2A7 /* IQUIScrollView+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB352C9155E142C44FAE86885DBC3E62 /* IQUIScrollView+Additions.swift */; }; 2396E8B97AED713A2F93792BB324738B /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C22B352D9B240DC40532BF2241C498 /* SkipUntil.swift */; }; 23BE46034D9F742CFFED1D6B7A25A2E1 /* RxTextViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3771D2D60EF94F8BBA0E912C27A80D24 /* RxTextViewDelegateProxy.swift */; }; 2478D027E97BFE0F71CF21D8B9E8020F /* RxSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D2C96C6D5355207398C46C9E66386BE /* RxSwift-dummy.m */; }; 2524EC4EF024A6C067A0FA082084CFB5 /* MJRefreshBackStateFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BF9B78A8693992A08F4F159CCDE50CA /* MJRefreshBackStateFooter.m */; }; 255C4ADB2BD82FEEF86A3D4D43F31C63 /* ISO8601DateTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4851621D756478D43208A461240D7CF /* ISO8601DateTransform.swift */; }; 25729D655583EBF1BDE99FF6BEAC272C /* RxSearchBarDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0197B6C172230E3E4655E94AFC0EFD32 /* RxSearchBarDelegateProxy.swift */; }; 25B9DC627989966BCF1DCECDABD78D23 /* RxGesture-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = E010BA57C8A6D94C6A65D63FA871BFCB /* RxGesture-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2615F7FE9AF4BF4955E9CB747E98FC21 /* AnimationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E8E6375D3923532919D7663A2689D73 /* AnimationConfiguration.swift */; }; 2616DFB5A134CFB3D2F88848CA6730A6 /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8A134B3C9407540FCDEC584183133D /* Rx.swift */; }; 2641C5F2AF734454E8A6DF94E606EE71 /* MoyaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F9BB5EBA26D7BF9CA0C3B21B1AAD9EE /* MoyaError.swift */; }; 267F1D36C8E6ED6B5CBD0DACA0FB38C3 /* UIImageView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF02AD8B77821AA7137D153FD7D26DC /* UIImageView+Rx.swift */; }; 27156C9092B7A82E24CF96312A941150 /* TransformType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73232742568F2DC4E279896B22BA8A3 /* TransformType.swift */; }; 2738EFBB546EAD1A60E9426A13D68CEB /* UIProgressView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C4B50F1957DB069477D1DF3B46EE040 /* UIProgressView+Rx.swift */; }; 273AF2009B201DC6C16AEF4030FFDFE6 /* ReactorKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1BC61193B3F56535040C0A61FA71C455 /* ReactorKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 282E6F0E67F3799B0ABCB9ED96B666A9 /* RequestModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BFA257EAD55FA150B56C6B292EAA44 /* RequestModifier.swift */; }; 283410C5EE7D346795416AC105C37733 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7897B0DA6B24D6557C87D44F527163A2 /* RxCollectionViewSectionedReloadDataSource.swift */; }; 28766FD0E20C59920C9235FF0BCB22A2 /* Then-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D6AD0538BC7F97DC3694296C53DBE9C8 /* Then-dummy.m */; }; 289F681251A15B7EF356F7198CB3A5CE /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEB5737B2DA2DF3423F29C0CBB7542DA /* Range.swift */; }; 29188F71B40A3F6A817C5369E3DBA7FF /* HasDisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4896403B4FA72B445E17270A138939D3 /* HasDisposeBag.swift */; }; 2950762546DE22AB539CDBD87B000951 /* UITapGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 589D959893A1EF698394AAC806F18758 /* UITapGestureRecognizer+RxGesture.swift */; }; 29560D9109C9140F67F3105A1DE0A5F8 /* TTRangeSliderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B86E459038ACF95C44868462505C56A6 /* TTRangeSliderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2968F27F99F394EABFF42645167C47A7 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7464D95C191D51D7944010316440FF /* Response.swift */; }; 29AEA44F673962935BCE359544C2EE0D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 2A194A7EBF3AF8D602791B5686534941 /* UIStepper+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB96EFEBACCC377C2C1852E1187BDE3 /* UIStepper+Rx.swift */; }; 2A1B0B5AAC6B121823473D167C5B372E /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA47B9E0DFFD8D15F19F5033E63B9026 /* CurrentThreadScheduler.swift */; }; 2A2FE216328BFC23AF9D30EAEFAA2303 /* IQKeyboardManager.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D58AEC2CC3F21D23D64B1106A4C82CA2 /* IQKeyboardManager.bundle */; }; 2A7AE49F85F20FEDA1833CAB32D7933D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 2ACB302D10B4EBDD404FE7781B7EB24D /* ObservableConvertibleType+Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91D5BAB4A332A7524C3625B57B61925 /* ObservableConvertibleType+Signal.swift */; }; 2BF0E4469C13CAA14361A3D5B07E1706 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 2C33DAACDC03995C3FEC4FA750B506CE /* SnapKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D2D55E77F429A861A4AEA5872801C81A /* SnapKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2C5FA28BC6344178AFF753F9BEEC172F /* UIPageControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B599D41042A76B9DF98EB3822B3056BA /* UIPageControl+Rx.swift */; }; 2CEA12D52F3F7AC0669C99A157E19F62 /* MJRefreshBackGifFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = B7AB99038CF5A8302F1BE2D35B09885A /* MJRefreshBackGifFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D628BD8A654993F779B1C05DC8A4F4C /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E72118C85ED536830E63BFEF402950 /* Debounce.swift */; }; 2D89AB26203628C04B3E87D045525701 /* MapError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE1D2FF0526260DCE037584DAB5A0D /* MapError.swift */; }; 2E024197B07B741D9A5BFB4CC13872FC /* Platform.Linux.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A82E326004C5937DB3D98AD29CEDFBA /* Platform.Linux.swift */; }; 2E1C908C7B515E06E6ACE5D56B2D0037 /* MainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E420D6F4781A96E122D216CBDA976615 /* MainScheduler.swift */; }; 2F288ED3641B2B4775884C462AE377E6 /* Kingfisher-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 36B939D525A7B01D959ECFCBDE9C0550 /* Kingfisher-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2F711ADC9F8930796D22855B9DD7AE42 /* RxPickerViewAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84378AB9BAB98B7273235CFA0707DE2D /* RxPickerViewAdapter.swift */; }; 306C95EBAA55143914CC0AAA88E769D1 /* GestureFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C5FAB0B3B58A21449A74F3BE0722D4 /* GestureFactory.swift */; }; 3077B4186B916A8DB980BBB72DF7FFB9 /* IQUIView+Hierarchy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EF8AC35D0E53603F8A97DBBE9FA33E2 /* IQUIView+Hierarchy.swift */; }; 3185243C5F5AD72D5DF9D3D7CEAD5237 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02ACAB88021BEBB2FCA8B6883F2CE332 /* ConstraintView+Extensions.swift */; }; 3244910D451D0BBF516D27D067C9A5C4 /* ShareReplayScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = E127D4A1CC515D5A1562D756ADE9089E /* ShareReplayScope.swift */; }; 32995C9F464270A5E2D36056E9B5190B /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553E666C8126DEAEA023EE677226741F /* LayoutConstraint.swift */; }; 339CF382B95EBAEAC534A4DB86412D49 /* UIApplication+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58674873A60E033E010D6D00276BA742 /* UIApplication+Rx.swift */; }; 340B17F6811B1F4D98C92209B4A68C5A /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB1570E1320634AB029069C7CA04D40 /* Buffer.swift */; }; 3433BF7613AB168C91A7310C0B5CE6A9 /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D465BD6EB72259B11615DB9761D3DB /* MultipartFormData.swift */; }; 344DB99F8332029C0391F08B6D0E425C /* MJRefreshAutoNormalFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D076E9D89814329C802E72B0C0229A2 /* MJRefreshAutoNormalFooter.m */; }; 34E6139A80CA53099913276CC5157304 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 356CB228C18677929A9A520E77620B13 /* RxCocoaObjCRuntimeError+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 809E8BEC46CF11685F9980428C93B446 /* RxCocoaObjCRuntimeError+Extensions.swift */; }; 35CE9431B032341D2B6C4FF4384C84AB /* RxTableViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6BE51ED2DAF09F29A0DDADC0911D49B /* RxTableViewReactiveArrayDataSource.swift */; }; 35D685CF7C6F73A65AD3DD99674CA78F /* MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = FC57AF3FEB7D99891C6F7BD5610269D7 /* MJRefresh.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3626B94094672CB1C9DEA32B9F9502E1 /* TaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE37D8A7C1180573189B3F4D4D5D2A0 /* TaskDelegate.swift */; }; 3666B742397E794ED820CC7AF304560C /* URLNavigator-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 504FC395E9A355EFA723008D25BE2E85 /* URLNavigator-dummy.m */; }; 36C33EC0952D217038E06134251A4267 /* NetworkLoggerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E3539D710126BA8CE5B7B6A9EBF994E /* NetworkLoggerPlugin.swift */; }; 377636521A7593EE0D162CA57DD21C2A /* RxTabBarControllerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D01082F46B5E949B212F6A27CDEF6DF /* RxTabBarControllerDelegateProxy.swift */; }; 3829CBCC3598DB13C2AD3B7383D1F24A /* MJRefreshFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0748670A15DF8EB5BA1C623F6B944876 /* MJRefreshFooter.m */; }; 383C9A8DB84B83233633C3A00A2C1608 /* KingfisherManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59830F33473DBCA365D965B7747764A8 /* KingfisherManager.swift */; }; 38F166338687E3EDD813EC86634B915F /* _RXObjCRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = B0D0CA0D4B6A0E2EB840F4E5D6C75340 /* _RXObjCRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3AF4EE85A4140EBA123BA54FBFC1ADF9 /* Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A25F9545112FB4EA2545D303AC81CFB1 /* Delay.swift */; }; 3AF8D0984A8BD088E7135F79FCA0AD78 /* AssociatedObjectStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A11C513330652C7580864E6FF559BC /* AssociatedObjectStore.swift */; }; 3B1C65D9BB266645228E3FF013A67A62 /* RxGesture-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BE0C2F5C5C4F8CD822C21482EBE49DD8 /* RxGesture-dummy.m */; }; 3D68B2DD0D49F92CFE4D07447D4861FA /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E6B1DB2C5390EA79CEFCA5BAC709AFF /* Generate.swift */; }; 3DA75181D1B7D20F0CDE54EA97C3072C /* _RXDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEC3152A51E8F66A48451B7A4EC9704 /* _RXDelegateProxy.m */; }; 3F7E121270CEB5622C8E10DE12E11E40 /* Platform.Darwin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17F817A4133AB7260E9700802CE54631 /* Platform.Darwin.swift */; }; 3F911616F29C285D5A4D7F534F4412FC /* Then-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F71B53FF12451C5503271983A3FE932B /* Then-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3FF87BDA510674F117F6EA54871A33F5 /* _RXDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 2534A262C08145CCB9C56D17DB32573D /* _RXDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 40092E84B6E1A2354BEDE901662715E1 /* TYCyclePagerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 01974B7A9277DCB1555881FFD8B7A8E1 /* TYCyclePagerView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 40765AAA791D6DFC0022639E348F0E6A /* FormatIndicatedCacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FDFBC7B8CD4E93779C4C76ACF38B06D /* FormatIndicatedCacheSerializer.swift */; }; 413C2A3759ABEC870AAE61F28C258C31 /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB757B34331F94A8CE2ABFE0AC0050D4 /* ImageDownloader.swift */; }; 41F2693C7BDA9AA4141C2017F724EB92 /* GroupedObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4600B7894931DEAFCABE02CD7AB268 /* GroupedObservable.swift */; }; 4215D7313CE6E738AAD80E9FEC6C60CC /* DateFormatterTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655359FE97777A5E6376898C32918AD1 /* DateFormatterTransform.swift */; }; 4257A939388AC78D0B3466520A06879B /* Debugging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D315F44377FFD3AEDE15898C2971BEC /* Debugging.swift */; }; 42C4C9573928B869632A7829D9A6BCBE /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502F0D4E9FE8FBC064C04982F146FE43 /* Queue.swift */; }; 4303F3EF9822CB9AEB720051B0FD4F6B /* Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 351865278BBE506A2B088B8755AD46BD /* Sample.swift */; }; 4323B40B40C5D9EF55A1E88569EE9C8E /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A276DD5300A0CD1F004D67179921FE96 /* AsyncLock.swift */; }; 43B97E40D05359125AA9EB9A928D53C7 /* SharedSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF66AEF465B9C56A648DA16DBF3D2E4E /* SharedSequence.swift */; }; 43D9EDAE797E70640D7B55EB56DA871E /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4CFCC4E9A965F95EE17202FBB5F310 /* SynchronizedOnType.swift */; }; 442CB576089010AAA46D4D5977388D87 /* Platform.Linux.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C5AF229FF21FF8786BD3CDF9E0BD504 /* Platform.Linux.swift */; }; 4546CA3500EDD39DBE8AD016CD79BDFC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 4639BFED317F19A1BA465B22250228A1 /* Materialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E417881CF3E530ABBE356AD806F81649 /* Materialize.swift */; }; 476CAF0EE5F9011302EF906E33A10687 /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA64CC4D23BCB33D713381874C6A93C9 /* RxTableViewSectionedReloadDataSource.swift */; }; 47C3E46E339F974D2B413D2F96BD02B7 /* Differentiator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7F044D3460E87057EB82CCDD5C4D545 /* Differentiator.framework */; }; 47CA831BE2229583488AE4A12E320BCD /* PriorityQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D68DEE1421B7ED892C4E0B33E9CC35 /* PriorityQueue.swift */; }; 481AB919527D0B7F4C6DBC8F84DBCDE0 /* TYPagerController-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0C35EACA9CABA73CF6D660DB55A64A /* TYPagerController-dummy.m */; }; 48700A675D816D1D4D07E26087D0A5C8 /* UITableView+RxReusableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3743F6F50232B4A27328B734F963F8 /* UITableView+RxReusableKit.swift */; }; 4882783C5576C5D43CE79C03DE33C3DD /* IQKeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507ED73099FD654BE7A82BBDAADE2C57 /* IQKeyboardManager.swift */; }; 4891FF4C052E475B1712F1251F9E3D9D /* ScheduledDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF04BEEB09DE1352D9F767144EF0FD50 /* ScheduledDisposable.swift */; }; 48EC78A50C61424D4DFDC33A325CAA72 /* ThreadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D8A0052E8AA49DDD883B85DEA3F90EF /* ThreadHelper.swift */; }; 49FC00F712C285D51C1C0708521BB01C /* Skip.swift in Sources */ = {isa = PBXBuildFile; fileRef = E018F18F377B9190734B1D9EB8160AD7 /* Skip.swift */; }; 4A54582B320910433D1A82740A41FAF1 /* MultiTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA8654A5097FB77F91981EC742DE882 /* MultiTarget.swift */; }; 4A79D8BBA62B2581089376DCF16AFD95 /* Then.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97927D56FAC9E735B7340A75B06C0600 /* Then.swift */; }; 4AD94A043ECE1C2D0B98F467E75D40C2 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1599175E64562E7F65DC8ECDF4F4E69D /* RxTableViewSectionedAnimatedDataSource.swift */; }; 4AFA156443C8EF0EDABD03E6626DAB5F /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D269397D68987A4E3F571B01394F0E /* Filter.swift */; }; 4CE7FCD6E10E191F7811CFB81CFDAF6C /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6D95EA842EF7D1BFA6D8191AAE620B /* Repeat.swift */; }; 4CECE91A2400A1389FB240DA09F92D44 /* First.swift in Sources */ = {isa = PBXBuildFile; fileRef = E415D1E19F77D7279C5F140411D69E67 /* First.swift */; }; 4CF6C6317C8C75CCE3E05761E0A923AC /* TransformGestureRecognizers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B157B779C8184526F0932ECBA9879A9 /* TransformGestureRecognizers.swift */; }; 4EA164050B9B1B1344A8984EC924D34E /* Disposables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49461972A7736ED5C10EA776E946211B /* Disposables.swift */; }; 4F03F93732A1DB9A6C1AD8ED0384DD90 /* _RXObjCRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = 96BF47DDEF17AC72F8EBF9CF0EA9DC04 /* _RXObjCRuntime.m */; }; 4F486A2A1239E02C62E27B6DB5EBF025 /* ConstraintMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424B56D99302A846FFD60288ED3225A3 /* ConstraintMaker.swift */; }; 507559646F241781DF5F959957D54C98 /* ConstraintDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A6B64332C20EFFA0F9444871E3166E2 /* ConstraintDSL.swift */; }; 512C561051AAF62FEAF39708C9E8273C /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E365162D0EA4A1CE302074F5079D99D /* ObservableConvertibleType+Driver.swift */; }; 51390FB969CB66F4D75C47503E00A1D2 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9CFFF8A56873944159E6B04DBAD45BD /* Deprecated.swift */; }; 5160BA1430D2179A43B3A0B558F23E1F /* Never.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5E590482B741DD275B9A87B7EAF4DA /* Never.swift */; }; 51B625C8BDBF7B247899FF64794370B8 /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0764078D276516AEF879D67E95D225E /* Changeset.swift */; }; 51D96676F916DC1EB1EEA5F91481E01C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E5F9481F27654B03C1A5A056D8909FB /* UIKit.framework */; }; 52416A6AC7387ECACCC45596498D96AD /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C719E3ABA8B6DE4B326AD2FF6313BA2 /* CFNetwork.framework */; }; 5288D9F32C3135D4FEF0A9601C153E7F /* PrimitiveSequence+Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09E2CE4824B4A44FE923169E5B3934C /* PrimitiveSequence+Zip+arity.swift */; }; 52D0B3F0EA5BCADED39162E5560909F9 /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BCF16201BDE699E2122416B0D2FBD6E /* ConcurrentMainScheduler.swift */; }; 5373AA3C95048D89FB975795EA34E8C7 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC01AC2D958140E2A090D7B632F5567 /* ControlProperty+Driver.swift */; }; 538546EBA321B92342C272F36B39C23B /* UILayoutSupport+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A8496A2160E940A6246AB063ADFA02 /* UILayoutSupport+Extensions.swift */; }; 5387216E723A3C68E851CA15573CDD71 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D62DD162002F9C56F1C29692DB9FB4 /* Request.swift */; }; 54B5B7D9E6EFC9C2C7B051BF214B9069 /* ConstraintPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CF8E77098D7789AB0555F960E5B430 /* ConstraintPriority.swift */; }; 551FB4DF0E5072BB849BB4FCE74F7361 /* HexColorTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 530479F817D071873ED5DC57B4372350 /* HexColorTransform.swift */; }; 5539A3CD2B653BA23D68AED9ADEA40C5 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A112A635DA48EF8CB77AF4F3AE614D /* Alamofire.framework */; }; 555AABD226E4FCBD91442DE5AED8483A /* Moya-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 737DA5252745C9E3883929C8A145E22E /* Moya-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55F024586B17FD47A9D6CACB88B6BED3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 5759B5BC5FA1E2C7B5FF69B511DF68DA /* SharedSequence+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DFDFC714EBFF20FC56B6534173B322 /* SharedSequence+Operators.swift */; }; 579D9C74D7293D142E36B5DB1CF0E320 /* SubjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089E5120665D4B93D03D218D3D11C2F1 /* SubjectType.swift */; }; 58523F5544BDE0B82DC6E63014A5638F /* TYPagerController.h in Headers */ = {isa = PBXBuildFile; fileRef = C0155AA3020891E25233E5B541EF2063 /* TYPagerController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 58E6E68D10EF3D64522BD267CC1761A6 /* NSBundle+MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = EA3EFB292391C42C14DBF00E49C533EC /* NSBundle+MJRefresh.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59109302859941E04E87DC7044DFEE89 /* ObjectMapper-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BC52351249DB2B467541996968B175A0 /* ObjectMapper-dummy.m */; }; 59AA965004671689A9B41955CB6BA511 /* DefaultIfEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA67E4E3F08A96BF5B2596ED0FC34196 /* DefaultIfEmpty.swift */; }; 59B16488BD43F14B60CFEAA70A67150B /* Platform.Darwin.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD70508C00E38F1FE63CED0383CAFE6 /* Platform.Darwin.swift */; }; 59BC49B15C7B4BD0AA38436D3567F9BD /* UITabBarItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4D4A9C4DD3E20DEF7AD0D414C67442 /* UITabBarItem+Rx.swift */; }; 5AC44C9876A3D343C63CDE76A4139036 /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F72D0F593F82201EA482E15012B694 /* ObservableConvertibleType.swift */; }; 5AE90602C51230C8DA03B35061E42AA4 /* Enumerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0785C89934FE4C79E378F80B3EAA947F /* Enumerated.swift */; }; 5B5D8AE167BD8E304CE474A9B8DBA2B4 /* Kingfisher.h in Headers */ = {isa = PBXBuildFile; fileRef = 6100112F9C1D863256E650CDE2B078D9 /* Kingfisher.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5B72B69DD603A056FE26772F3CFD56E8 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 266FFDCA364C275317DA17C75FA9B1B3 /* Result.framework */; }; 5BAEAF4DF2226C38891357D17E315D91 /* NSObject+Rx-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B8020BB1D97D4EA3246C9476D7044FE7 /* NSObject+Rx-dummy.m */; }; 5BB8EA4A2E5BEAB53FD3C34496F3CEDB /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66B23F5CD12DC047D454DFB36DAF64B7 /* AnyEncodable.swift */; }; 5C11C8A05966C040DCDE2E2B1B0C8D7A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 5C5A43AECB27D48D5C446D3DD12DC1AD /* BehaviorRelay+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86FCD05DCA4B5988057772E478AA3B7C /* BehaviorRelay+Driver.swift */; }; 5C6EBB68141BE6FE8A58CDAA7456BB67 /* UITextView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6367B989A05CB68EFF4B1822D8DB33E /* UITextView+Rx.swift */; }; 5D035B296631F978D172D0AE1EDF319D /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553ED5CCD42E1F512E1D9E9466E47D44 /* TakeLast.swift */; }; 5D448725D385F6D5B24C43CC9448F8E5 /* TYPagerViewLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = C5B39EAE40FAC88E85E0EDD5108794F0 /* TYPagerViewLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5D83206A9A4656F11D511158EC8E0784 /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76C763EC2CA9D13E7E93A029686393D1 /* CacheSerializer.swift */; }; 5D97452371AFE3FBC01EF1B84633C34B /* OperationQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70876246376D5A1D2C4ADF75F307186A /* OperationQueueScheduler.swift */; }; 5E30EEC06CCBCF67205D284B1FC7496F /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A098FD7778F9B758BBC03971A34449C /* SchedulerServices+Emulation.swift */; }; 5F59C423FBE9B7D79A9CDB48BCE24B3E /* ObservableType+PrimitiveSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C40C05B2CC135DAAF345CD1E46FE065 /* ObservableType+PrimitiveSequence.swift */; }; 608A03F75F4AC580AF9EBFD756AB8DC5 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9BB47909385E153DEAF4820584660C /* Window.swift */; }; 608E9ABBC7B02657C21A181EC25DED65 /* SharedSequence+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B840B6E9063F05A5F844D55042BC104D /* SharedSequence+Operators+arity.swift */; }; 609E700D79249C49A9EF7D6F72440F34 /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C651CFF068171CF1C1237A69E7D99A /* Diff.swift */; }; 60F43A8950189140E8E60155EA3F403F /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32CA5B611266CA013631C5E362ACECE /* ScheduledItemType.swift */; }; 61200D01A1855D7920CEF835C8BE00B0 /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74515FDED92421AF135AF3C0B8B08A1B /* DispatchQueue+Alamofire.swift */; }; 6129D5C1F81A4991CEFA556838395C58 /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF0A09A6DD2C0453F866A408223B631 /* Reactive.swift */; }; 621B413DAEFE75991251ED378822527C /* MJRefresh-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D52211FFC97D4BB7CF97DA504F823D77 /* MJRefresh-dummy.m */; }; 6298B1C2234864DA5ED3B5E6398B95AC /* MJRefresh.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0A7149E89BECF86F1AE3C3567AB16589 /* MJRefresh.bundle */; }; 62F65AD8DC4F0F9610F4B8B4738EC094 /* ServerTrustPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B89463432FAA55975050E17F0E6B42 /* ServerTrustPolicy.swift */; }; 6303CB98027B59C02EA4EFB216E7069B /* PublishSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DC026D0D8F36437F6F9662A1805D0E /* PublishSubject.swift */; }; 63C400B1C64EC85BCE1ADB5C1C869B95 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A7F6D1EC5B670FE3CF2E00D21C2D6 /* Driver.swift */; }; 641E1DF7AF5B9C72AAE1242C38A3F684 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = E03BFCA5D027189D74E5502CC0D58735 /* Logging.swift */; }; 643F0A0AEF6B1040E1243E8F065F0268 /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC294161F7CCD1280208FEC5EE254218 /* Driver+Subscription.swift */; }; 647C9A69799E80C9EFB477D0ED6D2406 /* FloatingPointType+IdentifiableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FF3BD6C6FED1A57B6B88C3F0EFEB6A3 /* FloatingPointType+IdentifiableType.swift */; }; 64FFC1F647245F61C405AE7AC1DA9D31 /* IQUIWindow+Hierarchy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D94ED6DD481751BB6F57C5B6C60049A /* IQUIWindow+Hierarchy.swift */; }; 6508C244210F686EA369416EA9FA88E9 /* CompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10FF295CB8B9A0152FA38DF372846A4 /* CompositeDisposable.swift */; }; 65CDABE73636295123CD8259D16ACFC1 /* ConstraintInsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 610AEB49CFD63698A27742F484C3B01B /* ConstraintInsetTarget.swift */; }; 66407236B5F3BF75ECBAE79FB585EC78 /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705EED60B52573B696D1FD8B6CB52FC2 /* ConstraintLayoutSupportDSL.swift */; }; 671B54E4AAC34ADFF8D1C372330E98FB /* ConstraintPriorityTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B37D88DF834AFA3231D9F6EF76598D8 /* ConstraintPriorityTarget.swift */; }; 674EB63D9FF75C7AB07BA9DA46D8AE09 /* RxDataSources-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E031F5913ADD2E4B2FE376BDDB99068C /* RxDataSources-dummy.m */; }; 677CCFAA3FB253A30288AF00D9C792AF /* UINavigationController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = D94169655CF6B1F44A9F5A11128DFBB5 /* UINavigationController+Rx.swift */; }; 67A4D8A24085ADC44A65404E73461B43 /* Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C08FCDD6B32CEDDEA0C67B874CFF29A /* Mappable.swift */; }; 67C42ADBD42CE7F614E2893EDA641519 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACE4EA986227C4D5F468E9FC4DB57BC /* DelegateProxy.swift */; }; 69D61F970DB381B6094C0E8085CB1E00 /* TYPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = E99B4913C9AF498445CF9BB785FFBFFF /* TYPageControl.m */; }; 6A0B1A0FA7A75C861B9D401D0E8CC95E /* MJRefreshGifHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D8CA91B2544EA3008663944FBDDBB5F /* MJRefreshGifHeader.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6AE889190D5DE14192E8E0D26E0EA3B9 /* RxTableViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 013A0244BEC843116DF0F443778476C9 /* RxTableViewDelegateProxy.swift */; }; 6B1D5FA190C5943AC83CBDA241C07E27 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F954956863F74706AA3A395F8401F14 /* Zip.swift */; }; 6BAE5A1DC9D8A6DED87FF11DEA68C04D /* Cancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70459A28397D1E419B4B3394FA8435F3 /* Cancellable.swift */; }; 6C2FE27FDBD6621AAB57E4082419C347 /* NSDecimalNumberTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3AFC24D86F7304F0B4AF511C9B9970A /* NSDecimalNumberTransform.swift */; }; 6C37C3E9F334B181ADEA8BDBF35751B5 /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 889059818A6183D31A0BDC7C6B981849 /* ControlProperty.swift */; }; 6CC98E6987E27D6CCDDFAE2F012A013C /* UICollectionView+ReusableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1DC57990281D4F45BE128AADCE4143 /* UICollectionView+ReusableKit.swift */; }; 6D5E66010A47E24AC918B48051FA9B3B /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F364B3A757735A453DB16E22F1A2D52A /* Lock.swift */; }; 6DB8DA4031D9691E8E809A385F4A5846 /* ReactorKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EADA7DDE939B839BF4574A8A38504C9 /* ReactorKit-dummy.m */; }; 6E116732E3E785C9F5D69045E6371E71 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAAE74B8E84AC1A31796559B46480858 /* RecursiveLock.swift */; }; 6E67DFDCD807369FC97F5F093ADCEB94 /* Differentiator-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E7C918776C8129D7372B1095C6E2BBD /* Differentiator-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6E953E2A94F99E02F79C4B98004E5396 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0580BE8F6F974685A933A89D8A972BDA /* Bag.swift */; }; 6F5E2DD4FF386EBB8EB55967964C5BE3 /* NSObject+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8F833CA4526A1FF65CC394962AD92E /* NSObject+Rx.swift */; }; 6FA0019746BB30AF58E427FD0CBCEE3C /* SectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2056BF365D2D7CC0FD46ED19EDBEE823 /* SectionModelType.swift */; }; 706840688BA7E9BDE5F4DD2C0E558835 /* EnumOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = E441DE19DC813E660F49CCA5AF0FA3F5 /* EnumOperators.swift */; }; 70F84C166016A96F0493CAE9BAA238F2 /* BinaryDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10806D65630D3922CC4632E4B961170 /* BinaryDisposable.swift */; }; 71BA33FC95D59E6C0FA6DEEF747351B7 /* CollectionViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5D2C3BA32F9BC3AA7C975B97F6A50 /* CollectionViewSectionedDataSource.swift */; }; 71DD71770F915F1621F0155CFD6CCEDD /* ResultProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8E64555EB2FE7D579467AABA52EB26 /* ResultProtocol.swift */; }; 71F6D96E3AEAA07389F37E2C4B63C4F6 /* MJRefreshHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 91BE41B89A55956B97FA90BA78DA1A4C /* MJRefreshHeader.h */; settings = {ATTRIBUTES = (Public, ); }; }; 72AFC537FCBE9326BB279F237C9043F6 /* TailRecursiveSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 701196C1B8D5354D685D7DD627877FEA /* TailRecursiveSink.swift */; }; 72F8287CD5471AE0376C28EC9AD903CA /* ObserveOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F34BA700CC37058B6A053DD142E65DB /* ObserveOn.swift */; }; 7335E13169D3E91E60ABF132EE768E99 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */; }; 73B9C996AED49ED7CF8EC2A6F1738059 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 74EBBCE7876A5AE3C73D7386EB85D4A4 /* Single.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C44514B1EC24A99AC7728B3503A038 /* Single.swift */; }; 75BD25BF1A16B849D520933C732BEE8D /* NSLayoutConstraint+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2B78D4FFAD19F5D58FA9B42B6E4F2FE /* NSLayoutConstraint+Rx.swift */; }; 75D28DC57BA76546CEDC5EFA5453E974 /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 861BD4AC5D5011049786E6352C62DC93 /* StartWith.swift */; }; 76407DBA37432054658558614EF3C6D5 /* InfiniteSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1530F4900EF841B8D75AB4AFA41076FB /* InfiniteSequence.swift */; }; 770E2F9E0A86F1290A34AD9C5FBA2128 /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A81EC5AF3F05041AE6EB4193B2BFEE3 /* SerialDispatchQueueScheduler.swift */; }; 7718718BA955BC1ADF13A14CF2FAEC0D /* AnimatableSectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F1EA6B598A844B871DB54C2C1C197D /* AnimatableSectionModel.swift */; }; 77CA4CD47748C0B69982A6D82C2799D5 /* Mapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541CB7596A9F5575EA8684328858A044 /* Mapper.swift */; }; 78393A7387E5886A01E30F08172D3963 /* ReusableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 188117C123963623C4A723C6D908FDF6 /* ReusableKit.swift */; }; 788910D5C544192C236243BF21CD1E78 /* RxPickerViewAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE766E158FAC90198034E79F61FB2CE /* RxPickerViewAdapter.swift */; }; 78C198E9BB56FB59BD494C8FA759645B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; 7942D242D079A5710A72B659A54D7E26 /* MJRefreshNormalHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C6E2980720E2A6CC91BB366BC6C7B2 /* MJRefreshNormalHeader.h */; settings = {ATTRIBUTES = (Public, ); }; }; 795ED1A351690C7143540C8CB7AA6CD0 /* Result-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D4A33E97EE17B95FCE36DEBE7ACE03F3 /* Result-dummy.m */; }; 79B3581182D487C5DBEF24D7E8B263BE /* URLRequest+Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85A799653D3BC7BA7BDB6BD691336F8 /* URLRequest+Encoding.swift */; }; 79F5CE53E58BED49036519F4E1197395 /* ConstraintLayoutSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20C215CD03F9EC325B96AD2A4621096 /* ConstraintLayoutSupport.swift */; }; 7A82F7317FDDA0BD35C5E1BD4BE42249 /* Binder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84F456A91C16DC64488D342C8BC5929 /* Binder.swift */; }; 7B0FACE97300F45175045FD090F70CE2 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 298536EB0ACF540DC4DC34F019837D74 /* Image.swift */; }; 7B50A3CE4086B03F60DCAFD8090DF164 /* SchedulerType+SharedSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCA82E849FDD3356D44EC7B8EE2BA84 /* SchedulerType+SharedSequence.swift */; }; 7B5FE28C7EA4122B0598738E54DBEBD8 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DD88365EFE5E0A8E363300ADCE2DCC /* SessionDelegate.swift */; }; 7C1AFD71632F9548563C3B224B6BFAF1 /* UITabBar+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9707233113E3D0E414A32F652E88D1D8 /* UITabBar+Rx.swift */; }; 7C8AABC35DC356D57A0766A331DAC4DF /* RxSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 387296DC1983BFD653D558D678637B29 /* RxSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7C98D625AAB36D8F2A92D5FAF28FCCF1 /* RxWebViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90D0F930F0AE0A2471269302003B8A5F /* RxWebViewDelegateProxy.swift */; }; 7CD0D1E5ED33F75E019C7D85E040FED2 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA5BE3862DD3A00CBB848A024A5E863 /* ControlEvent+Driver.swift */; }; 7D8CC01E8C9EFFF9F4D65406CDE0AB66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 812C7F916BBBCD2970C245989B39CD1B /* Result.swift */; }; 7DF2FFD108A489BAE76957B27C0815F2 /* SectionedViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41DF1FD6C9D2BDC29A48989F838D1ECD /* SectionedViewDataSourceType.swift */; }; 7EFDAA1642FA83DE777CFD086182D5A4 /* MJRefreshAutoGifFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = EC5873F3553D2C072C19D661AA844D1C /* MJRefreshAutoGifFooter.m */; }; 7F47526A3CC008FF7F1D3232D3435559 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */; }; 7F61C5C04977D2A2FF71DF6FEC861A94 /* RxCollectionViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9C2D5FD4A6E72AF207648A0646DCCF /* RxCollectionViewDataSourceType.swift */; }; 7FEE8F5AEE7AF4879B8911612CF82CCC /* ObserverType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834769F9B06E2DF4306C35CB6F967BAE /* ObserverType.swift */; }; 8014530DA6E561B4CA4905F2F68FF87C /* RxTableViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081D3C3389E6943F023464C1787FDC23 /* RxTableViewDataSourceType.swift */; }; 8069223DDD6D0F2E1A87D8E66966709B /* MJRefreshConst.m in Sources */ = {isa = PBXBuildFile; fileRef = 75AD8F0F482E2F585B2664D1635B4706 /* MJRefreshConst.m */; }; 80B0A06B1530CD62C0E818A58D30BCF8 /* UILabel+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE31A7BD0FFA90C66DA1DEBAD573E053 /* UILabel+Rx.swift */; }; 8151252BABBE1D0CF4828711B003BCE6 /* UIWebView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE7697FE2AC8BF4148D6676FBC1B5A23 /* UIWebView+Rx.swift */; }; 81A2F975614A9B94F265CD3BA816B0A4 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7481193578343285EC8F430B736D1314 /* QuartzCore.framework */; }; 82443926248B0468C2318B68ABB2FE14 /* TYCyclePagerTransformLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = C47049CF48166B10EF910F23995AEC84 /* TYCyclePagerTransformLayout.m */; }; 8253C04716B2C6BE88755512C4BB2D27 /* Differentiator-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C0DB238A8FB8D79A81E67CBB38ECF524 /* Differentiator-dummy.m */; }; 839A55AD3279D154DC55C93BE714B93E /* AccessTokenPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA052CCABE96871EBBAFD7D6C959A06 /* AccessTokenPlugin.swift */; }; 84F11AFCD46554A1844889EBF7B915A5 /* Observable+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA67591D1C61133E2F626EA1C2E097FE /* Observable+Bind.swift */; }; 866952184CCB9236E8D567A97A7C3224 /* IQUIViewController+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88CB6A2CAB2E3799803CD1C118F1F107 /* IQUIViewController+Additions.swift */; }; 866B48826FBBC43E96593CF27485FFC1 /* ConstraintLayoutGuide+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46E1355C3CBDC013D697081398F8B462 /* ConstraintLayoutGuide+Extensions.swift */; }; 86C74540C24DEB52302C80CA759169C8 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCDD91A5C3CBCA65A1E0746F0ABC174 /* Using.swift */; }; 873BA68C2E24D634D38E4A8BCB74B08D /* Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6B9B2770629FBEE8FF3829E91ACE5A /* Kingfisher.swift */; }; 8757DAF954F1D3C28C865BCFE62341D1 /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06DCA6A0B6616240EC75F089FCB605D /* AnyObserver.swift */; }; 87AD9D97F0F94C85720FC018235CD8A9 /* IQKeyboardManagerSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 973B74E176AD01454BE984AC4A7136A0 /* IQKeyboardManagerSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 87B96B16646AD96D837414B29D2E20C7 /* TYTabPagerBarCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D609A4B215C782073049E462F652D85D /* TYTabPagerBarCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88A25F661A4435D57041D21700515CC6 /* IQTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87874701D9E81D433E42E8AAABDFFD7A /* IQTextView.swift */; }; 891A61A41441BA89E0055C960A4664ED /* UI+SectionedViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53647AFE918247CC408B6EF13349D614 /* UI+SectionedViewType.swift */; }; 89DB813358F7013E9E1AAA822417FE86 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77AB7A6BBF44C678FD2D852835009017 /* RxCocoa.framework */; }; 8A1AF61FB826C9D132ABFC0195471179 /* RecursiveScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383DA86E343CBA75A7B8574AB75E1A1B /* RecursiveScheduler.swift */; }; 8A25F0A06E679359184DD5C9BE408190 /* VirtualTimeScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99692BFA25CF2437019950A2EFAE0291 /* VirtualTimeScheduler.swift */; }; 8B5BA7E0D60BA533FA43EF493F4D5D83 /* NavigatorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4923E32C7795B1406F85C0D769B8F1 /* NavigatorDelegate.swift */; }; 8B6ADD6C31B20E3FC60C00F494B59112 /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9B85640D4BCBBFE6662A71B6EE98AE9 /* ReplaySubject.swift */; }; 8B855A73B75155FB2C0CB05B54FDDE0D /* UIScrollView+MJRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BF627D8C19D8504043EEDE55963F4BA /* UIScrollView+MJRefresh.m */; }; 8BB9815D2C57EA5200DCE170988860DB /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EEF0C4EECF8525D00D4B8CF83ED7D3E /* ObservableType.swift */; }; 8BF6B3A9B39B75AA591626B7B26175DE /* ConstraintLayoutGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4086B02C434EDF398B3DEAC9194E36E9 /* ConstraintLayoutGuide.swift */; }; 8C8833BAF8EEDBF0491FC6CFEAF001E0 /* Typealiases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE2A49804E43DA5A796136D1D79109F /* Typealiases.swift */; }; 8CD28DDD291AB1B9D0F6DE817CE995FE /* UIGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C87EF44982A4D3803D88EB9983E52D /* UIGestureRecognizer+RxGesture.swift */; }; 8D0EC20E5A152C52E877A0FD42A1D612 /* RxSearchControllerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABBD78A9569D4426E15EAE838A7F4562 /* RxSearchControllerDelegateProxy.swift */; }; 8D5D644E475E21E418B2FBCC9423A471 /* RxDataSources-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B9B6B63EED3B0A8A6802499D6992A2CE /* RxDataSources-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8D6FC3FD5C6F9AB903E885369D8C437F /* ConstraintConstantTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D28FC1E29A3FEC5D1CF82FC68A09E26 /* ConstraintConstantTarget.swift */; }; 8D70BCBAC4C9EBC91472AB223B7485AE /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */; }; 8D7D42C25F9430F572E14200C399D57B /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6A0E89EEA59CA5659D2EED0C773FD1 /* ImageCache.swift */; }; 8DC3FC2342EC2E944B3A3E83E2683130 /* MJRefreshHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 203840BDEB077FAE0820685734949691 /* MJRefreshHeader.m */; }; 8DE01434EBF2D9040CF7BA7CBE33DD6C /* UIViewController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536B3E455CFA43C1E70A9AD9ACF3CD69 /* UIViewController+Rx.swift */; }; 8E3D1F07AFD7234A412DAAD8E22A9A09 /* VirtualTimeConverterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66AD8E23D6C4A5FFCFE4C066E700B676 /* VirtualTimeConverterType.swift */; }; 8E6BFD0EC4206F98140D4CA51D60D517 /* RxNavigationControllerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4602CBFEB0FDD72D7A2FF806152CFC81 /* RxNavigationControllerDelegateProxy.swift */; }; 8EA1993902CC8DE0A3E5F00E00976C4D /* UIDatePicker+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28EC587F2484F4548370BCE6AA22C0C /* UIDatePicker+Rx.swift */; }; 8EAB5DCA47437AE6A858FC683FBA23C6 /* Concat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26530B988040DFAD1200285B6F15D492 /* Concat.swift */; }; 8EC3A1FE52FACEFF6171B96607440D7C /* UISwipeGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59330F519A185D61B0CDC18A2BC8BEF7 /* UISwipeGestureRecognizer+RxGesture.swift */; }; 8F3F3F3792930B9DEDA544AAEA07A60A /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00B2D1E5594681E1384A988B35D66F6 /* WithLatestFrom.swift */; }; 8FB1637E28C8213F6A57DFBFC5390CAC /* URLSession+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FDDBEBD3900E6B6B3DE7D9D96C0CFA /* URLSession+Rx.swift */; }; 8FBA14759D5686E201066D8DD81D2794 /* RxAlamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 34903204D85860DD93734C3981A9900D /* RxAlamofire-dummy.m */; }; 902C28D80B247A6737276BF247BE1D1A /* UITabBarController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25A0BA6CC0B9439AAC6261DC60D7AE1C /* UITabBarController+Rx.swift */; }; 91C5F7C7350D7C7BAF11F0FFB2152C0C /* _RXKVOObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = E800F1732A0D7B4A6640BE266F14DDC8 /* _RXKVOObserver.h */; settings = {ATTRIBUTES = (Public, ); }; }; 93A094FED3649B43022FAD6174FBF11F /* RxTextStorageDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2E4D442D52B0894ED51620B7A11E8B /* RxTextStorageDelegateProxy.swift */; }; 93D444FC9D79520E14FB8EF178F0B76E /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB17D569CE831A4F4E00E2FC816EBBC9 /* ImageProcessor.swift */; }; 948045455233BAFB85BDF5F033DA8686 /* Take.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F80E196EA77F97C6AA8A3D2410BFE76 /* Take.swift */; }; 94E7616A9A6BBA3C0C59BE9F694AD549 /* UIControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E01EF7A9628B4C49AC12163B4FE6A91 /* UIControl+Rx.swift */; }; 9505BB9F7E1749994974BBD6D0A17CC8 /* NSObject+Rx+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F7DB8C08BAE7F26A0EE85DA33C3905 /* NSObject+Rx+RawRepresentable.swift */; }; 950D8E334E011B13CC5786595E6AE615 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D36179922B35EB5035CA039865F6B7 /* Image.swift */; }; 955203B31E419BFF9E9DC5EC02787D6B /* URLMatchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49DA791DE895721812C9EB83583324F0 /* URLMatchResult.swift */; }; 95C52B5A296DEDDA23804E0EB3441E98 /* CombineLatest+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280D08DD70717640589F62EB1F6FD19B /* CombineLatest+arity.swift */; }; 95DC0825394C10B2BCA74208761E16E6 /* ConstraintInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCA359E28EBA408A36C0296FA25A6731 /* ConstraintInsets.swift */; }; 96599CA3482E11E508F3607F678A4075 /* BehaviorSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59B6B580BF9608405256B589F993BA72 /* BehaviorSubject.swift */; }; 96E58EA8ACCB671460FB4D4A98113D92 /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1003DE1B3A476A1579EADFC5DEE4DC09 /* ObserverBase.swift */; }; 96EED7813D6F11E4A799B9B76F391500 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F86ADA13F9525995BA4102D3E1950B2 /* RxCollectionViewSectionedAnimatedDataSource.swift */; }; 9732F130B48FC5DDA707612D58A7BEC9 /* NopDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20CECB0BD149F46C10733B4F2D2D92DC /* NopDisposable.swift */; }; 97D88C924A7F6C1481FAECCC3E5E56E0 /* UITableView+ReusableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAD58DE19CDA807C841C29CE9804B80 /* UITableView+ReusableKit.swift */; }; 988CE455361E53CE47125E2E64F7279C /* HistoricalScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F3ADD7515A562BFBBBB8888ADA3090 /* HistoricalScheduler.swift */; }; 98F5BB1FF23CDD9DDAE74AE7DF766672 /* UIViewControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2988A2FCCBF291E4BEAC7F8D9AD45A9F /* UIViewControllerType.swift */; }; 9980108FD8F8C2F7FF71BD9DCAEAD6DF /* ConstraintMakerRelatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA2A35E8B50492B40B48AC0A94A8904 /* ConstraintMakerRelatable.swift */; }; 9983C08AB4621CB25D31ED64E3005A86 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = F570C010C51F2EDFA2B29E61A28988D2 /* Signal.swift */; }; 99B8666F8DEEAA2D94AF8F74CE63387B /* RxCocoa-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 58D1D78FFF97A04C18826C9383B3DFBD /* RxCocoa-dummy.m */; }; 99EE517D408612C2DD0FF21866BEAA63 /* NSObject+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BEAA51E3D065BAC60F1B85CBB1F26D5 /* NSObject+Rx.swift */; }; 9A726AE6822AF4B3AE67808BFB9845F9 /* DisposeBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0758B65380EFC2497BC08F9A7E4F402 /* DisposeBase.swift */; }; 9A8BECF84F3F50D260FB7132B311E932 /* ConstraintMakerFinalizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F0BF62BA5033111F22B49D2DA78D30 /* ConstraintMakerFinalizable.swift */; }; 9ACC05833323E3CB2FD5DEE095CFBD71 /* AsyncSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272D79A04BED524CBED07D2B4EC99B49 /* AsyncSubject.swift */; }; 9ACE31FCFAD5425ADF5B207722E20E8A /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B88F0CF34C9C21FA5FACA8A0692AA4A /* Deprecated.swift */; }; 9B9C1A095D68E3B91BD38F910C6F2E7F /* IQKeyboardManagerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F89941B737495BB6B9D65D0A940759 /* IQKeyboardManagerConstants.swift */; }; 9C178A996A6A0F14CF4B1F1D1BA4B2EB /* IQUIView+IQKeyboardToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E667F49AB3F41A361BA1B406783ABF /* IQUIView+IQKeyboardToolbar.swift */; }; 9D12124A0A7424D36B46A29F495E4394 /* RxCocoaRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = 64E69BA43FC5913EAFEEADEA145AFF1F /* RxCocoaRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9DE7A712CF1B72EBDF688D729F09EB2E /* RxCocoa-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = AF9EE6B5C17353C6D306E650D41F6A57 /* RxCocoa-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9E04C84E074D52269B2611658420FD41 /* RxCollectionViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C13EACC6D3C59A28B692EFB2DB14F6E /* RxCollectionViewDataSourceProxy.swift */; }; 9ED2BB2981896E0A39EFA365503F58CE /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7253973D128A903FF19D65F0A9DB9316 /* AFError.swift */; }; 9EE9EF9B3B2B22A38445D287017EFC0B /* Sink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D305720CFFC3323166DEE86CAF9341B /* Sink.swift */; }; 9F189CCBEDAF20A2F80F80B6F37CD18C /* TYTabPagerController.h in Headers */ = {isa = PBXBuildFile; fileRef = 88860B4DEDC2F32A5AC3E06DD5F534B0 /* TYTabPagerController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F75265E3A28C06EA75E5FEF00087551 /* TYTabPagerBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E49B1A15261BE86B3137451B245C6BE /* TYTabPagerBar.m */; }; 9FEDD419BBF24CFC1354161636496389 /* ReusableKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DD7CFB953733E5734A907460B2C40BCE /* ReusableKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; A006A7EEF7652FE9322878C39A36CED3 /* LayoutConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00A5B87228E30280657500960598B59 /* LayoutConstraintItem.swift */; }; A03ADFA996990D2DA836E6FFCEE37F36 /* MoyaProvider+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB53371318D8626F8F670FACB15EC0F0 /* MoyaProvider+Internal.swift */; }; A128DBCF691E0F4D2AC3EEA36F7BD562 /* ObjectMapper-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A332DD66083AD9F2BEB500FC7F9B60E /* ObjectMapper-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; A25F717B5E0C4E6A6DE933B0BF0F6417 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; A28F98C3C629B57C326A9FE9A51CE133 /* UILongPressGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500A7C34BE698156B2539B1AFD8A1AF3 /* UILongPressGestureRecognizer+RxGesture.swift */; }; A2A6F71B727312BD45CC7A4AAD7B0AB7 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3268ED5E6ABB0ABCDEEE48CE0CD78F /* NetworkReachabilityManager.swift */; }; A2D9F9EFFDFE9C19DA842E826B06BCEB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; A2E8853B9D79DAA9B2512F7AF251C587 /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 332E6B6D83344EA6F35D0681ADEAD1A7 /* Amb.swift */; }; A325A1B4D4D686789B291144FB283D47 /* TYCyclePagerTransformLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FC00E6B5EA00A0D7CA1DC61742ABA25 /* TYCyclePagerTransformLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; A32920098427AC9AA08B59797AAB0AA0 /* UIPickerView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = D819115F4043DA9F9126B97A2AAD84BD /* UIPickerView+Rx.swift */; }; A345A8C3AD50AA10257D9C7F63F7D5EA /* UICollectionView+RxReusableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D09ED075CFA4F5069E951F7B848972 /* UICollectionView+RxReusableKit.swift */; }; A36633C20A13B727FE2F135FB3261A24 /* Result-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 57EDA4292816BC724EE611C648E430DA /* Result-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; A46ECAD1FE903ECD9E035F9DB92CCEFE /* URL+Moya.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088517E9B0F7ED44CFEB3E14F7379BF5 /* URL+Moya.swift */; }; A4A7E0E7713372CEF2409C4B6E564837 /* MoyaProvider+Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1555063617BFE21D0E44B5255EA230FE /* MoyaProvider+Defaults.swift */; }; A5382D3EA98D2EB1AD9EDA5F2138F00D /* MJRefreshAutoNormalFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 40B36A7D5D671C2D2DCB124A4EE87A87 /* MJRefreshAutoNormalFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; A580F7392F3E5BD4B04688E9FE75B88F /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DAA4F672011BE9BD20D53B1B64E479 /* Disposable.swift */; }; A5BFC49ED41BC416BEBF4772517AB741 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E49107D99771943128C7B1A58B2781A /* Map.swift */; }; A5FF3C7A501FACB556E67C86C1990F7D /* DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 591CADBCCC57E88AE178D78B8606C938 /* DataSources.swift */; }; A6883C52C0D62642960508D2E64CB789 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59356F06A577C220AEBEDC900F94102A /* Bag.swift */; }; A6FD129851497CE8AB256289C1BD6463 /* TTRangeSlider-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A27C7B9B2AA2EBC3F3078495A3B4B248 /* TTRangeSlider-dummy.m */; }; A72D242BEF61E27D6C29092DF4666A4A /* MJRefreshComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 91EFB17C939EB1CE9FDE66074D20AA83 /* MJRefreshComponent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A788C5E58CFBB81C97345BAA4C6A292B /* Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D4430407B638D271F51E5D3E920BB51 /* Deferred.swift */; }; A7EC09EC6EFAB3778E0A0341E7DC9CE3 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20735E206ED10BEE6B25EC79F4DB6BFA /* AnimatedImageView.swift */; }; A8FC34B8104C504687DF452D1469CEC5 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A668A610822A5EACD5784C5EEE3E17 /* KVORepresentable.swift */; }; A9CA258A1B6D95AA156B0601E9473EF5 /* TargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08D8E58342C1CDB4D227FD9491D0E369 /* TargetType.swift */; }; A9DCAB1B8D538519A3E04A656155C163 /* TTRangeSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B85CEF090EDD7737A7E0F1D0F0F169 /* TTRangeSlider.h */; settings = {ATTRIBUTES = (Public, ); }; }; A9EEEA7477981DEEBC72432DE9990A4B /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A82133367E269E7AA71B9DB6052EFEEB /* Alamofire-dummy.m */; }; AA4959949A290D4B8BE1E31A890D0C80 /* MJRefreshFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8FF93E1E76B5ED1DA01941D734DB3531 /* MJRefreshFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; AA52CCA36AE2BBAA57E7DB6D3B437F6F /* NavigatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A310736267696B70A17786FD9CE22A3 /* NavigatorType.swift */; }; AA5D493C14AE53D874A31A847787D800 /* Scan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5ABD7D168407FE39CC76E2B5D13784 /* Scan.swift */; }; AACE8D1BD32A4820B7BEB0EE91FB0795 /* TYPagerViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BFCDA5713A7916617594A7D5B7BB187 /* TYPagerViewLayout.m */; }; AB9F26D130FF443CBE0DA2ADE2E27479 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77AB7A6BBF44C678FD2D852835009017 /* RxCocoa.framework */; }; ABB86D5B62EC96B6997FBE8361498297 /* ConstraintMakerPriortizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F92102426225DFFC6D512690BD4167B /* ConstraintMakerPriortizable.swift */; }; AC09A4779981FD4CD0F95DC2054E4C99 /* Reactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C74C02A940DA7E7A70BFF3EDB096E49 /* Reactor.swift */; }; AC1E75DFB1009851DBAA58FF21A18D95 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; AC8523DFDA128333C4B596BEE5DDD3BD /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC3EE36C7DE6D97F96B2B36B0E557C9 /* ScheduledItem.swift */; }; ACCE7F2F7E9C3F68256E49D4654E6076 /* TYTabPagerBarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 07A6E49BDE2308DCA0050930CA050850 /* TYTabPagerBarCell.m */; }; ADE879A06835C91322843C21A58C5C40 /* SingleAssignmentDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBA339974E545CDC23F472B6A4BD3E /* SingleAssignmentDisposable.swift */; }; AE1EF48399533730D0066E04B22CA2D6 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F766DA63DDEB880E468218435A871E /* SessionManager.swift */; }; AEF1257C803A37C026D774C6EB1F5A8B /* Bag+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F384E81160731D07A8472A16F0EF1EFD /* Bag+Rx.swift */; }; AF0CD2BD2A2074BF1A98000D204275A5 /* SwiftyColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D96AEEF8ACF586BFC5351394E36F02 /* SwiftyColor.swift */; }; AF2C3FA58842BEF584ABF0D15CD53311 /* TYPagerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E5085F3C34DD0D51CCDC43343115505 /* TYPagerView.h */; settings = {ATTRIBUTES = (Public, ); }; }; AF5D443F3631C6B95B4E8F831DCFDA79 /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CF3671CDFCBADF26426E2EC9ED34424 /* RxTarget.swift */; }; AF5F7A48DB63AB10243894FA9BA673A0 /* AnimatableSectionModelType+ItemPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A32996D04BF42DF4610DB92306B85695 /* AnimatableSectionModelType+ItemPath.swift */; }; B0033530A0EAABBB31CD46CA2899F3BD /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74AE307751AFEBCC18F1E034FE08A05F /* Box.swift */; }; B02D91E02099D3C6A2F988C766032176 /* MJRefreshBackFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = E4373BBA3EC79125E217AF8056418360 /* MJRefreshBackFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; B0B357445D024E50F897E2DE8F2D58AF /* TYTabPagerView.m in Sources */ = {isa = PBXBuildFile; fileRef = BE113770F3941380D55A0F946E7431DE /* TYTabPagerView.m */; }; B0EF72D8E2CB6594FEE4DB2600B32650 /* IQKeyboardManagerSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 51312E298DD4FE8C24D45F0F9149E1EB /* IQKeyboardManagerSwift-dummy.m */; }; B14375A0B7445CA462819C7468B3EF21 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E5F9481F27654B03C1A5A056D8909FB /* UIKit.framework */; }; B1A7ADAE2CAEDA5D703CE5C6A6DA405B /* IQUITextFieldView+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80810E76C51AA5F19CE1D4B0D55E060C /* IQUITextFieldView+Additions.swift */; }; B1C6D346149535E8CA5CD2443BBFCCC3 /* RxCollectionViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3508AF1777C25C4BC5CE6002832B9BF /* RxCollectionViewDelegateProxy.swift */; }; B2B98FD2647A3FA41300E78136335B3B /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCA6ED6120DA770AB86F564EEAC46DB /* Optional.swift */; }; B2CFCC48BC62FB4C35F6096233613F7A /* KVORepresentable+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC0E5E64017E2B544B9C4365BF0DBBF5 /* KVORepresentable+CoreGraphics.swift */; }; B2EFEFFB34D517F8E9A203B674AEBD13 /* DictionaryTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C07D23AF9D0ED48A9FEB791BA9FE25 /* DictionaryTransform.swift */; }; B33491A4893CF9905D4A8DF6A8BB52A1 /* RxMutableBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44D999B360E6504760FF4A8DD2CB17A1 /* RxMutableBox.swift */; }; B3643AFA39209852E984CD30BB18413A /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1778148BC4EB285B75595B087B8FC931 /* Queue.swift */; }; B3BC5178332C6FA8017CB00559E2AD92 /* UISearchController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D0CE43D178809120E722B457D98361 /* UISearchController+Rx.swift */; }; B3D2B497C3F6721D93B9D8C6E459B763 /* Moya+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD71B5E50804962A47932EB53600027A /* Moya+Alamofire.swift */; }; B467718840D63E04D0D3AD556E11E665 /* CombineLatest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4051F928839DBFCB64F5087D54C40E3 /* CombineLatest+Collection.swift */; }; B46EEAED7469BDA363E075F37016248A /* ConstraintViewDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = A468830C4A1700A0C17545C662543173 /* ConstraintViewDSL.swift */; }; B5A770DCA3CD54A8B48F6166F9D440E2 /* TYPagerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C82E28899508F47FDD43F9C7D4722F0 /* TYPagerView.m */; }; B65FCF589DA398C3EFE0128064E510EC /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC30C48A2548E77051CB43345238F5E4 /* MultipartFormData.swift */; }; B6E2FEDDD91D886E3BA3F1F9D338E672 /* Reduce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4914A9BB07C4CB2A6DDD5D0096A0BE87 /* Reduce.swift */; }; B74C2929B76A5841DBF3C73BA3C09FB8 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D4C9E3C57DE8A35333AAD4C4E744A0 /* SynchronizedDisposeType.swift */; }; B81E85BB5B30F34AAED56494D329A74D /* URLPathComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 306015976D0F37EFAC711062A258D8E9 /* URLPathComponent.swift */; }; B83AC06EC6750F396E898A09BE4BC42B /* ConstraintConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A7C9B0E3259DE8E0442244250D596F /* ConstraintConfig.swift */; }; B8567C825865356AF4137382CE6C007F /* Pods-RxXMLY-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 85302FAB5FE773C188E3758D45ABFF83 /* Pods-RxXMLY-dummy.m */; }; B8756B693A52F59BA955595B93A8AFE4 /* RxCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 339F05AA6561AB55D443E3626724120A /* RxCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; B948B6BD2A647A1DCC1401170B5D09FA /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3795E84704A9198DC6E04FB5A7227CCA /* View.swift */; }; B9AB0B01A8F7F6D3C83AEE614C9304C2 /* Moya-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C6994BF34E50D97EB111CDFB02DA22A /* Moya-dummy.m */; }; B9C39C539A810D1958AEEF1AEC0FD75C /* ImmutableMappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88CD1219127C85AAD3C013FB79D9BA84 /* ImmutableMappable.swift */; }; BA0711638621C2A47AABFF9E03546893 /* MJRefreshAutoStateFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 44409A050584D330FC1B6F6D3CD9B53E /* MJRefreshAutoStateFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; BABE350B5A22F812D599C5C016EAA1C4 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = C481BF3DA3A78A8E7F5176B802625CC6 /* Deprecated.swift */; }; BADF32BF243CAB2D89D072A0C2A9D791 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77AB7A6BBF44C678FD2D852835009017 /* RxCocoa.framework */; }; BAFB96EEF09DA050757292A2F5EA6404 /* ConstraintRelatableTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328686532CB38406B7BB3A32945B6474 /* ConstraintRelatableTarget.swift */; }; BB04209DDB72CD325AE09D2EC3EBC380 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 313319FF55255C3736F46998BCDF7F8A /* CoreGraphics.framework */; }; BBEFE2F9CEB73DC7BD97FFA66A0D9D4F /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7023AE71AB1962FA73C0D900E6564BE /* Validation.swift */; }; BC3A96095032E81BC2F8E5A479CE906E /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9946E97DCFB4572476564D33A05975E4 /* SynchronizedUnsubscribeType.swift */; }; BCEF70E63A638C19CEE89A53D7FFE523 /* ConstraintLayoutGuideDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434BF576D15902B83DA5D893260FA1D6 /* ConstraintLayoutGuideDSL.swift */; }; BCF96CF7173DBD9B80B2DA5EDB3A9119 /* URLPatchComponentMatchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E647F479FFCF502B2E7DF9A363BE038 /* URLPatchComponentMatchResult.swift */; }; BD2B90D397024A00B586B7A58994F534 /* ControlTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05D420155A7835B81FCFE92D3FC75464 /* ControlTarget.swift */; }; BD443E89A222B5234FE7167CC470030A /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495C0C539FEC03003AFB93904736DE91 /* Cancelable.swift */; }; BDB33C3708110C6D755175D1F0A2A70B /* MJRefreshStateHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 09B0C33CD96D9A514D7DB3610C6D2F6E /* MJRefreshStateHeader.h */; settings = {ATTRIBUTES = (Public, ); }; }; BDB43EE666BB8F17F18E4DDBBE6C2B43 /* Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9BA3DB7A5A422C613CFBFB2811FEA5 /* Timeout.swift */; }; BE13FF6639C26FFBFE305F57336AB8E4 /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98BFA9BCF11C38855F14FC8D710D4E95 /* RxCocoa.swift */; }; BE5C67A07E289FE1F9BE27335B159997 /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EEFED166A077BBCCE208D34F71B616C /* ParameterEncoding.swift */; }; BE7C78E3DD8046E3CB4BC5A28C057853 /* RxScrollViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEF74C2451A7C311E3C89E75D4746DD4 /* RxScrollViewDelegateProxy.swift */; }; BEE391D17D1ADB32DA7C8825FE827519 /* _RX.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FADD7C8C693834E93343EFB77D33AAD /* _RX.m */; }; C059355C2838DB2D4A2796B41543511A /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D231836443906AF664DE2289F32D387 /* SingleAsync.swift */; }; C084E86C68138F0227379212201B1D11 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */; }; C100B07C3830CD742E8C9D9E427B1E3B /* ViewTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F0AF2A7F102777266AB6FFCA689615 /* ViewTransition.swift */; }; C12639433CE2E21DEF3AA6DED99748FF /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C7719FC6A1EB8A6E7F08B0215685B3 /* RecursiveLock.swift */; }; C1446EC49AB387AF0EDF8DD292AB22F3 /* TakeWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BED0C58934B33DC41A1432BABD2EFA8 /* TakeWhile.swift */; }; C1A095D5DB6C70109EE936A914E53709 /* SwiftyColor-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DA9FEB5DF32D81B067986FD382163B1 /* SwiftyColor-dummy.m */; }; C1CF0A6316B4563F05B117FDABF76AD8 /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A31C1EE5C5A400DA67610DDECCBD49 /* CombineLatest.swift */; }; C1E5669E219DF70E0E763DD0468795BF /* KVORepresentable+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79A80A6FF76E139A757BFA4110503E7 /* KVORepresentable+Swift.swift */; }; C1E82097CF7DF3CB4B4063ED08D334D8 /* _RXKVOObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 957D3B23678E4698CC79A2DE9EA9F96A /* _RXKVOObserver.m */; }; C1F06F8B94A566ABE591F84CEAC03C14 /* UISlider+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B3623F446EB11D7EE2B1A505A7B1E5F /* UISlider+Rx.swift */; }; C2210016F369741901DB640E70B60688 /* UIViewController+TopMostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8F22838BEB780C855A8033AE7ED2A4 /* UIViewController+TopMostViewController.swift */; }; C26CD78814782F6C4BF7C3B02CA06642 /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC7B9021F90F58D6E367BA8B4C9C2B14 /* ToArray.swift */; }; C273C74217117E95DFB32C153F6DAA84 /* DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23FEC5DC4D59BF5FF4FE6E5DE5CF64BC /* DisposeBag.swift */; }; C2F4A31052D8F8C636829A7109A45DB2 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */; }; C3A7EF039070A3D3EE385B130DB94EEB /* Stub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71887F7534D1360E1CF05E79FDAC471 /* Stub.swift */; }; C3CFEB87CAB11F928DAD084B87C7F26C /* ConstraintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B1BE2330946471DDB4A3A11D43E0B8 /* ConstraintView.swift */; }; C40F6D2FE20755C045864C23F73260C8 /* IQKeyboardReturnKeyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6EC870FE5CE9FD966D23435C1DB6E0C /* IQKeyboardReturnKeyHandler.swift */; }; C446D343DA5F7767B64807C921A7BA4A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; C543308E999E593FE565FCE053658718 /* TYPagerController-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F4E3F42EFB30F56006250129E4BF5ABF /* TYPagerController-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; C5C6E5A84362150667FF88B6C18C0DAB /* SerialDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F08868A7AD5BECFD90CC22A62671DC82 /* SerialDisposable.swift */; }; C7298FEC2DD0BCFDF12F89121CA036A7 /* UISwitch+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4773B687AFC9012C6545F9C30E80356F /* UISwitch+Rx.swift */; }; C878E06DF50107B7362F7A9FD824E0A9 /* TYTabPagerBarLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = CD67BEEE3096E3E2BD09DF6158788A6E /* TYTabPagerBarLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9C7FF8901714AEEF66A96DEC28B4C51 /* Multicast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D21901F32D924DB95300B2C650103E1 /* Multicast.swift */; }; CA8174FE073791F18FB8C74BB2C4FF57 /* TYCyclePagerView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A5E925D1347A4A7AE44D07D3342D1BCB /* TYCyclePagerView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; CB29E86F679E9DDC5C5A1C89482701CA /* IQPreviousNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C114C6520E1FE3996B0B360BF559199 /* IQPreviousNextView.swift */; }; CB3BEC3D885EFDE2FE24B5A4F6FC16E7 /* PrimitiveSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7AF230FEF4F9203FE69CA75CAC56E33 /* PrimitiveSequence.swift */; }; CB4222BC34D92C2E06A5BCF730FEFE71 /* Kingfisher-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 347D8CF191232FE2DBCADBECC8AB9A5F /* Kingfisher-dummy.m */; }; CB6D60925223897FFA2662667DF83E8A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E6EA6ADCEAC27A4822132EBAC46122 /* Response.swift */; }; CB8DF027448816C623E9B02A3685A59F /* ImageView+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE96693365ED0583573F43D8F5B57187 /* ImageView+Kingfisher.swift */; }; CC3F2D5828482FEE2984ACA3FDF23349 /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A200DBF260A1DD96FA33642A921C9F /* ConcurrentDispatchQueueScheduler.swift */; }; CC93854C66A808596F5A3B58FDB49BF8 /* IQNSArray+Sort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B7D152E72F3D0808E4186445E4FC9B /* IQNSArray+Sort.swift */; }; CCBED63F97ED12D1D9F2A35E42056C7E /* PublishRelay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346F9291AC0BA83FB7B3962FC7F73267 /* PublishRelay.swift */; }; CD8C4C4267E198C30ED82159B3FB2A45 /* TakeUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5900BA1E16E8EB96D52F16B3133DD305 /* TakeUntil.swift */; }; CDAD14D7F2DC3BF192411C56B374C172 /* RxAlamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14F1C5DE23CD40C00D675D81FDE24A5E /* RxAlamofire.swift */; }; CDB48F0785D88989F4C7EB6C419AB6B0 /* String+IdentifiableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6586D8B7FB36BDE9E96FE450F51E98 /* String+IdentifiableType.swift */; }; CDFA17536632AE886DF2DD7FCBFF8F68 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B2C9F6C4EE9F9989261CF1F3418FC9E /* RxCollectionViewReactiveArrayDataSource.swift */; }; CE57D6E0880DE871AB922E0017E1472B /* NotificationCenter+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B06E614268F88A37528BE5B8A08933 /* NotificationCenter+Rx.swift */; }; CE7953BA2996D7B543684441E7BF2579 /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BBEE2DB22AD2B01DE2D742F08B660DE /* UIButton+Rx.swift */; }; CEA192A80E93599EFC5BEC6EE1F97BB5 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F172EFA622B78B8E948E1F153B1BDD /* Task.swift */; }; CED1FC24A094EABAF628E86E2BCBB656 /* TYCyclePagerView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E4CFC30F3E8C50C2772DDC74F2AC0626 /* TYCyclePagerView-dummy.m */; }; CEDBD76A18EEEDCA4EBAC563A97D2C51 /* ConstraintOffsetTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E2E1626B3D838551A7744F976FEB850 /* ConstraintOffsetTarget.swift */; }; CF3B98E3589779E82B30019BF13739D4 /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE22F0C823232387AAE225F52265246 /* BooleanDisposable.swift */; }; CFB93AB1B5504D35D1E6664972D62F19 /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F328B2BEE043C872339B6E419D2095 /* Sequence.swift */; }; D0121BBC81F4FA5DEF3D0A4F405BC605 /* ReusableKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B4592368CE0F4DED0B9CAF915FC9215 /* ReusableKit-dummy.m */; }; D02DEBC241C24DEEFDA13C355197A2FB /* UIScrollView+MJExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = FB8C71D5C39C423BB61425ABDC60B40B /* UIScrollView+MJExtension.h */; settings = {ATTRIBUTES = (Public, ); }; }; D044C46E53776D7072DB89497EC1AC73 /* RxPickerViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B97FA317AEF5791CA7299591278DDD85 /* RxPickerViewDataSourceType.swift */; }; D0E7D5B865722A25EC7D91B0FAD8A6CD /* MJRefreshAutoGifFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A0EE75F34E2C1B1E6254873E3066CEA /* MJRefreshAutoGifFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; D20061C845E39003EB86AB1292B4577D /* TTRangeSlider-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 18601CAC220AFC4C4A67E4C19A37FA0C /* TTRangeSlider-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; D371307D7540D73B2DC187E81186B097 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D11C85C7F13C6D98A3A465BC90BAD5C /* Utilities.swift */; }; D3BECB43898F07D286FCEB33DC197331 /* NSObject+Rx+KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A296320835E99AA5A0CED018A67A58 /* NSObject+Rx+KVORepresentable.swift */; }; D46545D206874E3A223372C84F1EA73E /* MJRefreshAutoFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 23859CF12D4131E1E0B8B0209A00B878 /* MJRefreshAutoFooter.m */; }; D500BAC2F56A14AA3226D7087700D426 /* ReactorKitRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = 385C967E6D18858D5311CA87A4205434 /* ReactorKitRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; }; D5CAE5F9ED6C5FFE2911D544A442B8DF /* UIScreenEdgePanGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5F22C193A3AD6545BDE4B91729668D /* UIScreenEdgePanGestureRecognizer+RxGesture.swift */; }; D6139FB200F1DB52CB02F86050D2C555 /* SharedTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D2C080BD70A34F38B01F04F8C066D6 /* SharedTypes.swift */; }; D66E78D63935A98894E702D922AA8B91 /* MJRefreshBackGifFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 43F0F183E8964F9C8D16E30F45CF6844 /* MJRefreshBackGifFooter.m */; }; D7B62E5B90F5CC105525D4E4A710A1F3 /* UIBarButtonItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98B0262ADD4A92D4CCFE4F51BE3FD5D5 /* UIBarButtonItem+Rx.swift */; }; D7F05F607F7BEE704777650F1F56A17D /* PublishRelay+Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB9B39409869FB19DBBE9E21A282D0E1 /* PublishRelay+Signal.swift */; }; D820DE54D6E9E5C85E0F9BF8A399E5EF /* MJRefresh-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 78266012E1D0050485CA537DD6F66FF5 /* MJRefresh-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; D947452217EE9CF7F39D5F25C226DACC /* CredentialsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB47246A17CA14228A79384E13C42EC /* CredentialsPlugin.swift */; }; D989E0CB9CB56EBC807116DB668019AE /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCCB28DE00E01DFCDBAFD966F967237 /* Catch.swift */; }; D9AAAFC710311CB167E506163E994A4D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B34630D7EB0A587B97952108987DB65 /* Observable.swift */; }; D9EAAE4A13D0E04E0557CA100CE0A2BF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; D9F2BA4D713DAC2A85CB4A453240EA3E /* MJRefreshAutoFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 38B56F8F2AC8700738ABD303DD4F3606 /* MJRefreshAutoFooter.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA7EB935B3E95B385300ECE8D38F71B6 /* Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1B85E14851EF923C2BD626BE22587 /* Create.swift */; }; DB2D2DBE410D12B4A8EC0DEFACF3DF32 /* Signal+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 622801585769EF5EF95DF31C3FC639A9 /* Signal+Subscription.swift */; }; DB87FF0D3B17ABB5452FDD4D70C9A84D /* FromJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 236D0EC42AF18C98E4FFEB08963E7C51 /* FromJSON.swift */; }; DB912D996DC177901DACA697F2EF96F9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; DBCCE10306122D22ABD5132028DEC06B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; DBF869BBD4D1B55C2D5CFE5EAE756F9F /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CF89A127DC79551BAA8988F82BD1E2 /* Event.swift */; }; DD2D9461BFEB9EE414A6D61E3A8CD7A6 /* Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = A678D15F6F0F97A723B186CEA25EE5A4 /* Zip+arity.swift */; }; DD311E4BCAE7DD4AA5C938968D72AAAF /* UIPinchGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569E77B7F0029A0A2087DE259C07DB22 /* UIPinchGestureRecognizer+RxGesture.swift */; }; DDAD3A47F10FBBB697E079CAB38EC31C /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4497D98DB94E983018E6F6C2A4DBAF68 /* Empty.swift */; }; DDDB37E35D9BC501950FB4909B609F2E /* ReactorKitRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F70D589B9E780A60A3716F46A4ECF0D /* ReactorKitRuntime.m */; }; DDDF354A21835294F7CFB5ACFA5EA968 /* NSObject+Rx-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 8533AEEE8CD59BA624CA9267423D28E5 /* NSObject+Rx-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; DE17E4C2C6EADE5D2D531268B1695405 /* UIView+MJExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 02FA95A68EC5AC5D4C787DD8DAA5C118 /* UIView+MJExtension.h */; settings = {ATTRIBUTES = (Public, ); }; }; DE8B3FC90EA6A88A4B44039572481C86 /* IdentifiableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3869DF0902D254BD40D6B02E11A8B52 /* IdentifiableType.swift */; }; DF1BF64A08EBF0DE6CD67ED0D49CB07B /* Navigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084AF2E51A268DBF8C3B947B137318FC /* Navigator.swift */; }; E027FBCDAF65778317E634F4A92512B9 /* TableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1C6F4433BDCCAB44D788A1B10A5011 /* TableViewSectionedDataSource.swift */; }; E0C9EB4ED4DC98789196DD61345ED42A /* TYTabPagerBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F27A8AA53C280B6DF033E2515D7A2E9 /* TYTabPagerBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; E1ED57DFDC84B588DCFF41921D9BED15 /* TYTabPagerBarLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = FA64F8A7EA7F1D019BA4637FA772850D /* TYTabPagerBarLayout.m */; }; E272024F3BB38967141313D5CB8E4120 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; E2F35C82BD4AB36C18F735165A7683EE /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DB87787AD0F7333249395D1378FED63 /* AnonymousObserver.swift */; }; E36C37C4AC1FFD475C4490193C66FF22 /* IntegerType+IdentifiableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CA5A419783AFF7463EA6144B0C1E36 /* IntegerType+IdentifiableType.swift */; }; E37B94CD6DB302F6D233BD878772B868 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */; }; E3EF505A67060CFB3E503F5B79D6E909 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; E402F8791AB3E6BE18F05C1265668896 /* ObservableType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90107D958C65D510896483F2CB8060BA /* ObservableType+Extensions.swift */; }; E454C46205FD054E2133897B1CF339D8 /* RxPickerViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9096DCC6CC66370C372F38BF8C1E435A /* RxPickerViewDelegateProxy.swift */; }; E47F2B7C9621969D29330632BF46DFE3 /* UISegmentedControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645CC45F2863AE7D2A6FB2A8C23E620F /* UISegmentedControl+Rx.swift */; }; E4C77EA0646C587F18D7AEC063AA13AA /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87CE09E0A8341C183F59A37440D5153A /* InvocableScheduledItem.swift */; }; E560CBBCB2A8426232A0C237818CB350 /* ImmediateSchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A5AC992E4B703AA08D0A815AAED779 /* ImmediateSchedulerType.swift */; }; E57312010AFB591EE37870F65E651EA6 /* MJRefreshNormalHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 77D21DAC0D06E0D6BBF5A78CB767E815 /* MJRefreshNormalHeader.m */; }; E678304A73835950C93570CD42BF160F /* UIGestureRecognizer+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64ED938103FC90F798F7A60F04981575 /* UIGestureRecognizer+Rx.swift */; }; E710FF1F4EB4B125F9C5DBDFE3B9320F /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F68D0C4E903F4B9E77676032554A5278 /* Placeholder.swift */; }; E77D52EE7D897833F4A8441A8F2477B5 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A112A635DA48EF8CB77AF4F3AE614D /* Alamofire.framework */; }; E80DB5D9B2D35085282DF057BB540AFE /* MJRefreshBackFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D4BA91A34AD8C308124A2BAC574B3B9 /* MJRefreshBackFooter.m */; }; E898E416B77BB93F9D6E37227C723D81 /* URLMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98DBDF6250E6EDD9D4711CCA72CDE3 /* URLMatcher.swift */; }; E90FA56D8781CEEECA7E5AD108103434 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397F2D1B6C0CB8DEFA63186BB12618B2 /* ElementAt.swift */; }; E92322FB9B976A926D52D12C5ECC5816 /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC94DB3E4DBB1ECF4FA33EBC9EAACFDF /* Switch.swift */; }; E97BD9D3A62BD8225C79AB2D4C4F16B3 /* RxAlamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A93AB57B4326570AC0951E305DD04B1F /* RxAlamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; E9E28E9BEA02135CFBF9E598CC1885D2 /* ConstraintMakerExtendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0768925949DA79DC0DB78B89D10ECB45 /* ConstraintMakerExtendable.swift */; }; EA326BB6A8ACA0EE3C0B44849198ADE6 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BF26D84DAD5A55C5E5BE6A0660FAEEF /* Array+Extensions.swift */; }; EA4F7446E804B5FD266D16B9E9AE5374 /* SectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923B40C1B730134DE3FB93D56434F359 /* SectionModel.swift */; }; EA67527CAF7ED571C8A84B66DDD70FBE /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 115F5111E88B41835988258D0B9D90F2 /* Operators.swift */; }; EA811EC31868E728B8BE88F94CA53B19 /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542762FC821F584B23860CBBBAF13564 /* AddRef.swift */; }; EAB9A201A0338EEBD326E78E366EEC20 /* ItemEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE8FF0C7C94471EB15B40751856B8D53 /* ItemEvents.swift */; }; EADFC7E11D0CCEE93B2F2B90B02C8CF7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; EB2866D890A7B0D65C8AB7F37614C15F /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0112A7EDEEB67FF3C434A3F925DEA88 /* SubscriptionDisposable.swift */; }; EBEB72F80DCD10DEB82BF5F856D467FD /* MJRefreshConst.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C7E38D158EEE0908532959AA8DCB439 /* MJRefreshConst.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC0418D76D4ADA0C663EE5E8D2D9B57C /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125CF19DCF28A39EC761EA281EBBC44B /* Plugin.swift */; }; EC1A896625476730D64BF9645624BBAD /* DataTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC29C744A1F98EEA2D40646969A57510 /* DataTransform.swift */; }; EC2CCB07B8CCEF669FAF345D29691030 /* IQKeyboardManagerConstantsInternal.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB7E27C2C9842A03F3BD947DD8493A2 /* IQKeyboardManagerConstantsInternal.swift */; }; ED4CDFA145F591A93A57DC1B7CE1C12E /* MoyaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0049E24D8DFDDC0E246DC35576E7DCFB /* MoyaProvider.swift */; }; EDA4B2824B12E7A3579F99909744E23C /* RxTabBarDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C755463902805FE86A327D9AD0B26200 /* RxTabBarDelegateProxy.swift */; }; EE20C85515D53DE61D8435080677F58E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E5F9481F27654B03C1A5A056D8909FB /* UIKit.framework */; }; EE7B1C447D0CF446C179EA74E5646A94 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; EE855D5B4F46A1DB974602210A435D93 /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06147002706BB75CECECA91480E5C0E6 /* ConnectableObservableType.swift */; }; EF12BF89DE7448AEE5CE6F0F8DCA3F9A /* UIPanGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F4C6BA01C9A3737D53D5029569BD25 /* UIPanGestureRecognizer+RxGesture.swift */; }; EF298B1BEAB1956FCF7E1E8EA3911780 /* UIScrollView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE68236572EEA4E94A38244743901FE9 /* UIScrollView+Rx.swift */; }; EF3D53302381F94241804E5CD95AA64F /* SwiftyJSON-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DD33D08EA36F2C8861A36D8E15B3610 /* SwiftyJSON-dummy.m */; }; EF412419B6C1CD59B3BBC3836BD43767 /* View+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F46744125C99B20BDE8928C9D91767D /* View+RxGesture.swift */; }; EFCDB86F2D87E50B9B3EAA6827FF4012 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C41A4EDAFACF2FCF4214E703CD5C47 /* Result.swift */; }; EFD264FC408EBF3BA2528E70B08DDD94 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187ACC537834C3D22473C47D21364850 /* Notifications.swift */; }; F07FA872B5DE108E5F0B486E7F183E77 /* Do.swift in Sources */ = {isa = PBXBuildFile; fileRef = E74A11FE1DBDB7EA6BDB703F6B58F460 /* Do.swift */; }; F1733F37EB2E6EF05781BB4DA571B204 /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2CEBF6D286D3D808C8AB81A58AFF59 /* Debug.swift */; }; F34B7F0D30A6D72E9FBF2242712BDF9D /* HistoricalSchedulerTimeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76F782371F7D6EF97DE209867481EA5 /* HistoricalSchedulerTimeConverter.swift */; }; F363D1083A5EFBE7CE56067520330A9D /* ItemPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459BCD44D36BC348042325F44BD78250 /* ItemPath.swift */; }; F39A3624015D4234E6FE35761F53BC93 /* ActionSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9647D7ACC10E74465531ADEEFD4D2E0B /* ActionSubject.swift */; }; F3E97A8E1D1ED2B3D24961995923B99A /* SwiftyColor-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 52A5AC3A3FC014CA05565755B1794774 /* SwiftyColor-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; F43A145ECD909D880FDC70E638AB8C13 /* UIRotationGestureRecognizer+RxGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F707D328F3FE6B0BA81F88374DC441C /* UIRotationGestureRecognizer+RxGesture.swift */; }; F48DD4AF7C254B7A2F67A690AF8EF143 /* IQBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E6990B20E9EA105BBB88B21C57593B5 /* IQBarButtonItem.swift */; }; F4988BB5862C381854506C82E43136A3 /* ConstraintMakerEditable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222AACF264926205DACCB10D9C8464D7 /* ConstraintMakerEditable.swift */; }; F4A57F7C1F8184A6DDEABB07FA6D7083 /* NSBundle+MJRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 09517CD921BEB56F91C5FA7988170302 /* NSBundle+MJRefresh.m */; }; F4ECE99FE56932FCA2EB2B238C8EA43E /* Optional+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60A46D85400354A02A72584F53237D8 /* Optional+Extensions.swift */; }; F527B0A072DFD754B471E4C1F6B16C48 /* URLTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9979EBBDCDD9A7DDDB0725FEC97660E7 /* URLTransform.swift */; }; F64167E53EFA6CC3537752E4C2442990 /* IdentifiableValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA8C0C17C65F493BBDD9F7C413C2C378 /* IdentifiableValue.swift */; }; F66F21FCAD37F04AF3CE0B9ADDFACCC3 /* UITableView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFBBB6581E245F03FD7746F2B3F6838A /* UITableView+Rx.swift */; }; F68EA720B0ED1BF96CEBF07977A1C0A2 /* MJRefreshComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 118F0C07C32E76EF6489509A4F07F50B /* MJRefreshComponent.m */; }; F6BECD98B97CBFEBE2C96F0E9E72A6C0 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 720B12F497653C86DA2AF27BCCDEFC2D /* ResponseSerialization.swift */; }; F71155830BCEEE6D69E12F14089ED538 /* UIActivityIndicatorView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C3B1C0259FBCD7A10525F73D370CF5 /* UIActivityIndicatorView+Rx.swift */; }; F71C6DC478982AEAFDE2B7040BC82D67 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04FE7430ECAF1467968440D35165D54B /* Error.swift */; }; F740D92AE1DF1156C9BE899D7A4FE12A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */; }; F793B5FB9A2D4726AF9CB4E564BFE650 /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A3DB0E144693D175CBA841C8B10601 /* RetryWhen.swift */; }; F809B2628CA2797393525F37F639FB7C /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A962B38EA206CD0B32DEC9E6DC414479 /* ControlEvent.swift */; }; F8B3D3092ED0417E8CDF32033F6122F5 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD95565DA23CF770293505264420AE0 /* Alamofire.swift */; }; F8DB73FCAA437CAC5D12AFBA1DD3D205 /* TYCyclePagerView.m in Sources */ = {isa = PBXBuildFile; fileRef = E1D44E21411F23F3505F31B444A1A104 /* TYCyclePagerView.m */; }; F91B75BFF4738F36668933C397A9FCA8 /* _RX.h in Headers */ = {isa = PBXBuildFile; fileRef = B717B3469C2896926EF05EAF11498DA0 /* _RX.h */; settings = {ATTRIBUTES = (Public, ); }; }; F931EEDE05DA8151D729C5C39F5CBE83 /* AsSingle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA6CC0C558EDC9E6881C22246E9E059 /* AsSingle.swift */; }; F95FC708ACF2B7648DFBCD974ACD9B89 /* DistinctUntilChanged.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1C4965E4534F094C7E5D0417A804AC /* DistinctUntilChanged.swift */; }; F98CB7FADDB6FDFD66E71D447444F5C5 /* RxPickerViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4284532690122048902F462ECE7D36F5 /* RxPickerViewDataSourceProxy.swift */; }; F99792883819ADBD16123F91F6A9F3F8 /* ImagePrefetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B48B219E8CC99707DBEA39F8FF5389AA /* ImagePrefetcher.swift */; }; F9B63BDA169DB048D41DA8395261AEBA /* DateTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B86B4FEA51A1E51158BB9084C301356 /* DateTransform.swift */; }; F9B8AF86D7F4375E5CDE51D06BE78501 /* Zip+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A088E5F2C74178B8F5D416C973214422 /* Zip+Collection.swift */; }; FA632862F1071D6B51FC158FA50367D8 /* RxGestureRecognizerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CD9A0FE14A09A30BF80B2ABA5E19488 /* RxGestureRecognizerDelegate.swift */; }; FAC3C67720C7E6EF51CCAE58F62645BC /* UIButton+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF47A0081987D5825B05757F83159AE9 /* UIButton+Kingfisher.swift */; }; FB27B9C1DE7B9384AB45D8BBDECD8034 /* UIAlertAction+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69D61C111C8F4287DFE589110A04EA4 /* UIAlertAction+Rx.swift */; }; FB640D6E1FCD811D905A620B508C33CE /* BehaviorRelay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A42C76F3823E2EFA148B18E55C8D6B2 /* BehaviorRelay.swift */; }; FB9C6242B11049E2F1062D04565E568D /* UIScrollView+MJExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B8B57244416BAE5506E83DFBCCE1F45 /* UIScrollView+MJExtension.m */; }; FC66B4AE1897AF442BDFF005497ECF87 /* DelegateProxyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA4D4657D80847325EA00819DC11ACD /* DelegateProxyType.swift */; }; FCE734BD83FB03D3DC9F8AA843ECD73B /* EnumTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F4BBE33D9DA445DD902A8A15991A56 /* EnumTransform.swift */; }; FD11E5E7BE23D70131359C06A7C63B94 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954CE4894A117D9DA14AE5D47A6BAE88 /* Map.swift */; }; FD1D0607A2C283A73BF4568A7076EA30 /* TTRangeSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = A46A36F4BF38056EAA48578282F54B60 /* TTRangeSlider.m */; }; FD31283E2A43955C5BA6FA9008EE8D7C /* MJRefreshGifHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = C68244972029A1CB9C966D064AB6967F /* MJRefreshGifHeader.m */; }; FD99B1C2C751D10EFC95B148A9028316 /* AnimatableSectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83BF88F0362AAB340061528783A19B27 /* AnimatableSectionModelType.swift */; }; FE782FADB39D1A5F08B981F6F7D27726 /* StoryboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0CBF488AAC85E03EDD426C11452A46 /* StoryboardView.swift */; }; FFE963243CB86C7F5CC3F386683393FA /* GroupBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0ECA86834476786492EC1E52EB3E13 /* GroupBy.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 15F1D0A9F74EAF7D407DC563D31027A7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 844C9B244CEFA2F26288789C423101B4; remoteInfo = Differentiator; }; 1A8E520D805AA6588B14DC77528D8B0F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 64CC9E274EBD6C16C53FC82EAA6FE9E7; remoteInfo = "NSObject+Rx"; }; 1B9585FC3126D0BD21B2221D625803E7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; 23784648AAF575118683F20D78A60594 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; 2ACD2B5E0B881C8AE65F933BD84DB2BB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 8E38AFA40EB24AAD3998F3F9CFB12CA3; remoteInfo = SnapKit; }; 2BA940D9885781F490DE5D7E8AF779C7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = E14A62900910444707A5504D25EC98DC; remoteInfo = MJRefresh; }; 338578A6E2DF40FE81DEC3B1372FB4AC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 0F6A8E28D6C2CC2BCA0477C6AF7137F2; remoteInfo = ReactorKit; }; 3AEB60BF032C5F152E4CCAEF2273CF19 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; 51642FCC15CB2105630B74B65E122C2C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; 5421BFF2151DBD1E4557434A332B44FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 1F5BFD2D8E3672CE19EA16B5208A4066; remoteInfo = RxCocoa; }; 6261BDDB54F9C51D2C747DE59337227D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 844C9B244CEFA2F26288789C423101B4; remoteInfo = Differentiator; }; 6BF334AD50FD77D652D687BCC4AEE011 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 6F712943B3C5592E82604940D11CE08A; remoteInfo = Kingfisher; }; 6CEBF919355CD7B687C5D986511A3630 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 93FD28A3DC61A9094248D0D96CAD9FD4; remoteInfo = SwiftyColor; }; 80D52ABA9C03D8C4E466A3C2ECC923CF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 88E9EC28B8B46C3631E6B242B50F4442; remoteInfo = Alamofire; }; 8DE395CD515C5D9F68A4FBEC6D9E49FB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 1F5BFD2D8E3672CE19EA16B5208A4066; remoteInfo = RxCocoa; }; 9264E39D9FF475826031CA00737738D8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 88E9EC28B8B46C3631E6B242B50F4442; remoteInfo = Alamofire; }; 9A4961597B182A9A8365503B12A4CC49 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 1C61647CEE01897368F5D0D2CC181DB4; remoteInfo = Result; }; 9B2B284C2F27B08C57FFC9EE798CE2AB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = E86A9F4C831EEE122A82C7CE19D4011B; remoteInfo = TTRangeSlider; }; 9FCB8BF28633D811B92390B57E58E0F8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = C76585AF9502709B32A11133189DDA23; remoteInfo = URLNavigator; }; A2D41E2E3A703CF7EC447F13B1D2D28D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = C235F77E101E742652CCF98FFDFA5182; remoteInfo = IQKeyboardManagerSwift; }; A344436CFCCEA3FD967A5C394C074F4A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 1F5BFD2D8E3672CE19EA16B5208A4066; remoteInfo = RxCocoa; }; A7230AE231CD9B06EE4CDAE3A1068E9D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 524DC20BC58D4BDA1F0C77763DFA70BE; remoteInfo = ReusableKit; }; A7D03FCA25AACE15F81E2A98FE9DC3D8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; AEB2857C08385E6AA8DBB9F8736DAF9F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = CD6C9FB6B052E1CF663F8A56041EA163; remoteInfo = ObjectMapper; }; B40C0E974B9564F38F538252683B1ABC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 1F5BFD2D8E3672CE19EA16B5208A4066; remoteInfo = RxCocoa; }; B5DD30A0B9289F764F95945D2D1F8C8B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = CCEEA2E517C9CB166C75BAF4A263A7CF; remoteInfo = TYPagerController; }; B6AEA89E672C6C293F8A451E760DC0B9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = F05F33A12963EC1A726FEE15D9000AE2; remoteInfo = RxDataSources; }; C1809C26AE9AE581B69F3EBF3020386B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; C291EBE2993E897C362199B1BAEEC035 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 88E9EC28B8B46C3631E6B242B50F4442; remoteInfo = Alamofire; }; C8D1C0BFABDA878088C177C8EE78FF8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = D382A7B90B59D72908CC21054785B020; remoteInfo = Moya; }; D7D399EDABE3916260C38946E0F4CCDD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = B496A58A7B812A10E0039E15EDF0E8EE; remoteInfo = RxGesture; }; DCDD2C4EA666E90B5DDFEEBBA1DC5C8F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 2D5C20681FC0A1E18576D4673D0EFE65; remoteInfo = Then; }; E09A273BEF2F739232571736BCC24375 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 2166B105EDC9D2499513E6D6BCD586C2; remoteInfo = RxAlamofire; }; E6C01306761AF7F0DB6473C9C03ECFBD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = DE874A99677D06A11E5FCC43C238A7D5; remoteInfo = SwiftyJSON; }; F78D05F575692EA731781B820137FCAC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 1C61647CEE01897368F5D0D2CC181DB4; remoteInfo = Result; }; FC9E7142C6CF7EE64F13E5BEE4B234E1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 0B620384669DABB34C3C5B96EA60F12A; remoteInfo = TYCyclePagerView; }; FEA62B82099C97A42F3968BAAD232850 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; FF663F02F48AB40164A87D8914B81DF4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; remoteGlobalIDString = 371551DFD98A9103D43168E4BD0479F0; remoteInfo = RxSwift; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 0049E24D8DFDDC0E246DC35576E7DCFB /* MoyaProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MoyaProvider.swift; path = Sources/Moya/MoyaProvider.swift; sourceTree = ""; }; 013A0244BEC843116DF0F443778476C9 /* RxTableViewDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTableViewDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift; sourceTree = ""; }; 01974B7A9277DCB1555881FFD8B7A8E1 /* TYCyclePagerView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYCyclePagerView.h; path = TYCyclePagerViewDemo/TYCyclePagerView/TYCyclePagerView.h; sourceTree = ""; }; 0197B6C172230E3E4655E94AFC0EFD32 /* RxSearchBarDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxSearchBarDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift; sourceTree = ""; }; 01A8496A2160E940A6246AB063ADFA02 /* UILayoutSupport+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UILayoutSupport+Extensions.swift"; path = "Source/UILayoutSupport+Extensions.swift"; sourceTree = ""; }; 01D96AEEF8ACF586BFC5351394E36F02 /* SwiftyColor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyColor.swift; path = Sources/SwiftyColor.swift; sourceTree = ""; }; 02242206E1BBE2B18A7CA031B7102D98 /* Differentiator.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Differentiator.xcconfig; sourceTree = ""; }; 02ACAB88021BEBB2FCA8B6883F2CE332 /* ConstraintView+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ConstraintView+Extensions.swift"; path = "Source/ConstraintView+Extensions.swift"; sourceTree = ""; }; 02FA95A68EC5AC5D4C787DD8DAA5C118 /* UIView+MJExtension.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MJExtension.h"; path = "MJRefresh/UIView+MJExtension.h"; sourceTree = ""; }; 039485063DF511DC852BBCD65C474C9B /* Timer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timer.swift; path = RxSwift/Observables/Timer.swift; sourceTree = ""; }; 04FE7430ECAF1467968440D35165D54B /* Error.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Error.swift; path = RxSwift/Observables/Error.swift; sourceTree = ""; }; 0545D09F28CBBEE994F089B68636507A /* Pods-RxXMLY-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-RxXMLY-acknowledgements.plist"; sourceTree = ""; }; 0580BE8F6F974685A933A89D8A972BDA /* Bag.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Bag.swift; path = Platform/DataStructures/Bag.swift; sourceTree = ""; }; 05D420155A7835B81FCFE92D3FC75464 /* ControlTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ControlTarget.swift; path = RxCocoa/Common/ControlTarget.swift; sourceTree = ""; }; 06147002706BB75CECECA91480E5C0E6 /* ConnectableObservableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConnectableObservableType.swift; path = RxSwift/ConnectableObservableType.swift; sourceTree = ""; }; 0748670A15DF8EB5BA1C623F6B944876 /* MJRefreshFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshFooter.m; path = MJRefresh/Base/MJRefreshFooter.m; sourceTree = ""; }; 0768925949DA79DC0DB78B89D10ECB45 /* ConstraintMakerExtendable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerExtendable.swift; path = Source/ConstraintMakerExtendable.swift; sourceTree = ""; }; 0785C89934FE4C79E378F80B3EAA947F /* Enumerated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Enumerated.swift; path = RxSwift/Observables/Enumerated.swift; sourceTree = ""; }; 07A6E49BDE2308DCA0050930CA050850 /* TYTabPagerBarCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYTabPagerBarCell.m; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerBarCell.m; sourceTree = ""; }; 081D3C3389E6943F023464C1787FDC23 /* RxTableViewDataSourceType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTableViewDataSourceType.swift; path = RxCocoa/iOS/Protocols/RxTableViewDataSourceType.swift; sourceTree = ""; }; 084AF2E51A268DBF8C3B947B137318FC /* Navigator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Navigator.swift; path = Sources/URLNavigator/Navigator.swift; sourceTree = ""; }; 088517E9B0F7ED44CFEB3E14F7379BF5 /* URL+Moya.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URL+Moya.swift"; path = "Sources/Moya/URL+Moya.swift"; sourceTree = ""; }; 089E5120665D4B93D03D218D3D11C2F1 /* SubjectType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SubjectType.swift; path = RxSwift/Subjects/SubjectType.swift; sourceTree = ""; }; 08A296320835E99AA5A0CED018A67A58 /* NSObject+Rx+KVORepresentable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSObject+Rx+KVORepresentable.swift"; path = "RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift"; sourceTree = ""; }; 08D8E58342C1CDB4D227FD9491D0E369 /* TargetType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TargetType.swift; path = Sources/Moya/TargetType.swift; sourceTree = ""; }; 08EF34F0793697F59142C4D8F3A0D159 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = ""; }; 09517CD921BEB56F91C5FA7988170302 /* NSBundle+MJRefresh.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+MJRefresh.m"; path = "MJRefresh/NSBundle+MJRefresh.m"; sourceTree = ""; }; 096040CC61B2863666482591D451AA38 /* Just.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Just.swift; path = RxSwift/Observables/Just.swift; sourceTree = ""; }; 0971AB82F462A7C2F67101AD9407905F /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 09B0C33CD96D9A514D7DB3610C6D2F6E /* MJRefreshStateHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshStateHeader.h; path = MJRefresh/Custom/Header/MJRefreshStateHeader.h; sourceTree = ""; }; 0A332DD66083AD9F2BEB500FC7F9B60E /* ObjectMapper-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ObjectMapper-umbrella.h"; sourceTree = ""; }; 0A7149E89BECF86F1AE3C3567AB16589 /* MJRefresh.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = MJRefresh.bundle; path = MJRefresh/MJRefresh.bundle; sourceTree = ""; }; 0A81EC5AF3F05041AE6EB4193B2BFEE3 /* SerialDispatchQueueScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SerialDispatchQueueScheduler.swift; path = RxSwift/Schedulers/SerialDispatchQueueScheduler.swift; sourceTree = ""; }; 0A8EF0EB23267E967663F5FA5A8E7686 /* RxSwift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RxSwift.xcconfig; sourceTree = ""; }; 0B86B4FEA51A1E51158BB9084C301356 /* DateTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateTransform.swift; path = Sources/DateTransform.swift; sourceTree = ""; }; 0BF26D84DAD5A55C5E5BE6A0660FAEEF /* Array+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Array+Extensions.swift"; path = "Sources/RxDataSources/Array+Extensions.swift"; sourceTree = ""; }; 0BF9B78A8693992A08F4F159CCDE50CA /* MJRefreshBackStateFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackStateFooter.m; path = MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m; sourceTree = ""; }; 0C40C05B2CC135DAAF345CD1E46FE065 /* ObservableType+PrimitiveSequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ObservableType+PrimitiveSequence.swift"; path = "RxSwift/Traits/ObservableType+PrimitiveSequence.swift"; sourceTree = ""; }; 0D076E9D89814329C802E72B0C0229A2 /* MJRefreshAutoNormalFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoNormalFooter.m; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m; sourceTree = ""; }; 0D21901F32D924DB95300B2C650103E1 /* Multicast.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Multicast.swift; path = RxSwift/Observables/Multicast.swift; sourceTree = ""; }; 0D231836443906AF664DE2289F32D387 /* SingleAsync.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SingleAsync.swift; path = RxSwift/Observables/SingleAsync.swift; sourceTree = ""; }; 0D305720CFFC3323166DEE86CAF9341B /* Sink.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Sink.swift; path = RxSwift/Observables/Sink.swift; sourceTree = ""; }; 0D8A0052E8AA49DDD883B85DEA3F90EF /* ThreadHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThreadHelper.swift; path = Sources/ThreadHelper.swift; sourceTree = ""; }; 0E5085F3C34DD0D51CCDC43343115505 /* TYPagerView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYPagerView.h; path = TYPagerControllerDemo/TYPagerController/TYPagerView.h; sourceTree = ""; }; 0E5F9481F27654B03C1A5A056D8909FB /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 0E79B5AC02467F0FC951B7F0808BE955 /* RxCocoa-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxCocoa-prefix.pch"; sourceTree = ""; }; 0F27A8AA53C280B6DF033E2515D7A2E9 /* TYTabPagerBar.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYTabPagerBar.h; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerBar.h; sourceTree = ""; }; 0F46744125C99B20BDE8928C9D91767D /* View+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "View+RxGesture.swift"; path = "Pod/Classes/View+RxGesture.swift"; sourceTree = ""; }; 1003DE1B3A476A1579EADFC5DEE4DC09 /* ObserverBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObserverBase.swift; path = RxSwift/Observers/ObserverBase.swift; sourceTree = ""; }; 1039A64D7973971C931057162135E4B5 /* ObjectMapper-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ObjectMapper-prefix.pch"; sourceTree = ""; }; 10DC35A2E29CA2B46E24A2F022E9163B /* ConstraintItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintItem.swift; path = Source/ConstraintItem.swift; sourceTree = ""; }; 115F5111E88B41835988258D0B9D90F2 /* Operators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = Sources/Operators.swift; sourceTree = ""; }; 118F0C07C32E76EF6489509A4F07F50B /* MJRefreshComponent.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshComponent.m; path = MJRefresh/Base/MJRefreshComponent.m; sourceTree = ""; }; 125CF19DCF28A39EC761EA281EBBC44B /* Plugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Plugin.swift; path = Sources/Moya/Plugin.swift; sourceTree = ""; }; 12F89941B737495BB6B9D65D0A940759 /* IQKeyboardManagerConstants.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardManagerConstants.swift; path = IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift; sourceTree = ""; }; 137E979916631AD9ACADADEEEBBBAE5B /* SubscribeOn.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SubscribeOn.swift; path = RxSwift/Observables/SubscribeOn.swift; sourceTree = ""; }; 14223DB3BAAFC3650B856F3ED4CA3293 /* Merge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Merge.swift; path = RxSwift/Observables/Merge.swift; sourceTree = ""; }; 14D38200F2B2C8E328B6E393A63B99D2 /* Pods-RxXMLY-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-RxXMLY-frameworks.sh"; sourceTree = ""; }; 14F1C5DE23CD40C00D675D81FDE24A5E /* RxAlamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxAlamofire.swift; path = Sources/RxAlamofire.swift; sourceTree = ""; }; 1530F4900EF841B8D75AB4AFA41076FB /* InfiniteSequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InfiniteSequence.swift; path = Platform/DataStructures/InfiniteSequence.swift; sourceTree = ""; }; 1555063617BFE21D0E44B5255EA230FE /* MoyaProvider+Defaults.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "MoyaProvider+Defaults.swift"; path = "Sources/Moya/MoyaProvider+Defaults.swift"; sourceTree = ""; }; 155A351F30BA1128E99E067F81CB72B2 /* IQToolbar.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQToolbar.swift; path = IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift; sourceTree = ""; }; 1599175E64562E7F65DC8ECDF4F4E69D /* RxTableViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTableViewSectionedAnimatedDataSource.swift; path = Sources/RxDataSources/RxTableViewSectionedAnimatedDataSource.swift; sourceTree = ""; }; 16A6BD5743A5A18E98F1E4D0B05E9282 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1778148BC4EB285B75595B087B8FC931 /* Queue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Queue.swift; path = Platform/DataStructures/Queue.swift; sourceTree = ""; }; 17CB2FA1178A4F2E225AE19BEFEB3B81 /* Pods-RxXMLY-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-RxXMLY-umbrella.h"; sourceTree = ""; }; 17F817A4133AB7260E9700802CE54631 /* Platform.Darwin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Platform.Darwin.swift; path = Platform/Platform.Darwin.swift; sourceTree = ""; }; 18601CAC220AFC4C4A67E4C19A37FA0C /* TTRangeSlider-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TTRangeSlider-umbrella.h"; sourceTree = ""; }; 187ACC537834C3D22473C47D21364850 /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = ""; }; 188117C123963623C4A723C6D908FDF6 /* ReusableKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReusableKit.swift; path = Sources/ReusableKit/ReusableKit.swift; sourceTree = ""; }; 18A31C1EE5C5A400DA67610DDECCBD49 /* CombineLatest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CombineLatest.swift; path = RxSwift/Observables/CombineLatest.swift; sourceTree = ""; }; 195D6043A1C7C9E2F93F03BF5A6C765E /* ReactorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1A0CBF488AAC85E03EDD426C11452A46 /* StoryboardView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StoryboardView.swift; path = Sources/ReactorKit/StoryboardView.swift; sourceTree = ""; }; 1A0EE75F34E2C1B1E6254873E3066CEA /* MJRefreshAutoGifFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoGifFooter.h; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h; sourceTree = ""; }; 1B3623F446EB11D7EE2B1A505A7B1E5F /* UISlider+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UISlider+Rx.swift"; path = "RxCocoa/iOS/UISlider+Rx.swift"; sourceTree = ""; }; 1B6E2178443AA8FE2901D19303B3B8F9 /* ConstraintMultiplierTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMultiplierTarget.swift; path = Source/ConstraintMultiplierTarget.swift; sourceTree = ""; }; 1BC61193B3F56535040C0A61FA71C455 /* ReactorKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReactorKit-umbrella.h"; sourceTree = ""; }; 1BCF16201BDE699E2122416B0D2FBD6E /* ConcurrentMainScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConcurrentMainScheduler.swift; path = RxSwift/Schedulers/ConcurrentMainScheduler.swift; sourceTree = ""; }; 1BFCDA5713A7916617594A7D5B7BB187 /* TYPagerViewLayout.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYPagerViewLayout.m; path = TYPagerControllerDemo/TYPagerController/TYPagerViewLayout.m; sourceTree = ""; }; 1C02566F282B7374FFE638BE27D65EBC /* NetworkActivityPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkActivityPlugin.swift; path = Sources/Moya/Plugins/NetworkActivityPlugin.swift; sourceTree = ""; }; 1C458E0D7CD6F0DD83AC852BC8419AD7 /* ConstraintRelation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintRelation.swift; path = Source/ConstraintRelation.swift; sourceTree = ""; }; 1C719E3ABA8B6DE4B326AD2FF6313BA2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; 1C74C02A940DA7E7A70BFF3EDB096E49 /* Reactor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Reactor.swift; path = Sources/ReactorKit/Reactor.swift; sourceTree = ""; }; 1C7E38D158EEE0908532959AA8DCB439 /* MJRefreshConst.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshConst.h; path = MJRefresh/MJRefreshConst.h; sourceTree = ""; }; 1CB7A49991CF5D7E632585BCFD130C56 /* RxDataSources.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = RxDataSources.modulemap; sourceTree = ""; }; 1CD95565DA23CF770293505264420AE0 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; 1D315F44377FFD3AEDE15898C2971BEC /* Debugging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Debugging.swift; path = Source/Debugging.swift; sourceTree = ""; }; 1D3CBDD7BD1351D05617EDFD0171017D /* String+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+Rx.swift"; path = "RxSwift/Extensions/String+Rx.swift"; sourceTree = ""; }; 1DB825CD3DE4C8C88F1EB6E75E3046B2 /* SwiftSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftSupport.swift; path = RxSwift/SwiftSupport/SwiftSupport.swift; sourceTree = ""; }; 1E1DF457C00960360E254419271FA76F /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1E49107D99771943128C7B1A58B2781A /* Map.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Map.swift; path = Sources/Map.swift; sourceTree = ""; }; 1EE2A49804E43DA5A796136D1D79109F /* Typealiases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Typealiases.swift; path = Source/Typealiases.swift; sourceTree = ""; }; 1EF8AC35D0E53603F8A97DBBE9FA33E2 /* IQUIView+Hierarchy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIView+Hierarchy.swift"; path = "IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift"; sourceTree = ""; }; 1F5B9A2F08507775E806FE12ED90BAF6 /* TYPagerController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYPagerController.m; path = TYPagerControllerDemo/TYPagerController/TYPagerController.m; sourceTree = ""; }; 1F80E196EA77F97C6AA8A3D2410BFE76 /* Take.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Take.swift; path = RxSwift/Observables/Take.swift; sourceTree = ""; }; 1FA2A35E8B50492B40B48AC0A94A8904 /* ConstraintMakerRelatable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerRelatable.swift; path = Source/ConstraintMakerRelatable.swift; sourceTree = ""; }; 203840BDEB077FAE0820685734949691 /* MJRefreshHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshHeader.m; path = MJRefresh/Base/MJRefreshHeader.m; sourceTree = ""; }; 2056BF365D2D7CC0FD46ED19EDBEE823 /* SectionModelType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SectionModelType.swift; path = Sources/Differentiator/SectionModelType.swift; sourceTree = ""; }; 20735E206ED10BEE6B25EC79F4DB6BFA /* AnimatedImageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedImageView.swift; path = Sources/AnimatedImageView.swift; sourceTree = ""; }; 20CECB0BD149F46C10733B4F2D2D92DC /* NopDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NopDisposable.swift; path = RxSwift/Disposables/NopDisposable.swift; sourceTree = ""; }; 20D269397D68987A4E3F571B01394F0E /* Filter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = RxSwift/Observables/Filter.swift; sourceTree = ""; }; 20DFDFC714EBFF20FC56B6534173B322 /* SharedSequence+Operators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SharedSequence+Operators.swift"; path = "RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift"; sourceTree = ""; }; 2135AEA738411503ADADD4D2497AA4E6 /* TTRangeSlider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TTRangeSlider.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 21805A4E1E5079259A7A86A6D073E71B /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = ""; }; 222AACF264926205DACCB10D9C8464D7 /* ConstraintMakerEditable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerEditable.swift; path = Source/ConstraintMakerEditable.swift; sourceTree = ""; }; 223F91E90B67ADF004A7E4543864C0E4 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 22B5011A39DF5849503132CEE075CC1E /* TYTabPagerView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYTabPagerView.h; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerView.h; sourceTree = ""; }; 236D0EC42AF18C98E4FFEB08963E7C51 /* FromJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FromJSON.swift; path = Sources/FromJSON.swift; sourceTree = ""; }; 2382EF1B0260B04756F14D3A5B99D192 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23859CF12D4131E1E0B8B0209A00B878 /* MJRefreshAutoFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoFooter.m; path = MJRefresh/Base/MJRefreshAutoFooter.m; sourceTree = ""; }; 23E72118C85ED536830E63BFEF402950 /* Debounce.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Debounce.swift; path = RxSwift/Observables/Debounce.swift; sourceTree = ""; }; 23FEC5DC4D59BF5FF4FE6E5DE5CF64BC /* DisposeBag.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DisposeBag.swift; path = RxSwift/Disposables/DisposeBag.swift; sourceTree = ""; }; 24B87BC6C657291DEE2644EFCB9CA341 /* TYTabPagerController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYTabPagerController.m; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerController.m; sourceTree = ""; }; 2534A262C08145CCB9C56D17DB32573D /* _RXDelegateProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _RXDelegateProxy.h; path = RxCocoa/Runtime/include/_RXDelegateProxy.h; sourceTree = ""; }; 25A0BA6CC0B9439AAC6261DC60D7AE1C /* UITabBarController+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITabBarController+Rx.swift"; path = "RxCocoa/iOS/UITabBarController+Rx.swift"; sourceTree = ""; }; 26530B988040DFAD1200285B6F15D492 /* Concat.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Concat.swift; path = RxSwift/Observables/Concat.swift; sourceTree = ""; }; 266FFDCA364C275317DA17C75FA9B1B3 /* Result.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 268594CEB578B4D18F4F1B30652F617B /* URLNavigator.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = URLNavigator.xcconfig; sourceTree = ""; }; 272D79A04BED524CBED07D2B4EC99B49 /* AsyncSubject.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AsyncSubject.swift; path = RxSwift/Subjects/AsyncSubject.swift; sourceTree = ""; }; 280D08DD70717640589F62EB1F6FD19B /* CombineLatest+arity.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "CombineLatest+arity.swift"; path = "RxSwift/Observables/CombineLatest+arity.swift"; sourceTree = ""; }; 28C85F1DBF1BC0BBAE450D1FBEA54A9F /* URLConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLConvertible.swift; path = Sources/URLMatcher/URLConvertible.swift; sourceTree = ""; }; 28FD4835CF32D5E0B1A2A3753A09DBB0 /* Moya.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Moya.xcconfig; sourceTree = ""; }; 298536EB0ACF540DC4DC34F019837D74 /* Image.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.swift; path = Sources/Moya/Image.swift; sourceTree = ""; }; 2988A2FCCBF291E4BEAC7F8D9AD45A9F /* UIViewControllerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIViewControllerType.swift; path = Sources/URLNavigator/UIViewControllerType.swift; sourceTree = ""; }; 29C099BC759B6B4145E48DE537580E13 /* Throttle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Throttle.swift; path = RxSwift/Observables/Throttle.swift; sourceTree = ""; }; 2A9BB47909385E153DEAF4820584660C /* Window.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Window.swift; path = RxSwift/Observables/Window.swift; sourceTree = ""; }; 2B34630D7EB0A587B97952108987DB65 /* Observable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Observable.swift; path = RxSwift/Observable.swift; sourceTree = ""; }; 2B88F0CF34C9C21FA5FACA8A0692AA4A /* Deprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Deprecated.swift; path = RxCocoa/Deprecated.swift; sourceTree = ""; }; 2C114C6520E1FE3996B0B360BF559199 /* IQPreviousNextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQPreviousNextView.swift; path = IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift; sourceTree = ""; }; 2CE37D8A7C1180573189B3F4D4D5D2A0 /* TaskDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TaskDelegate.swift; path = Source/TaskDelegate.swift; sourceTree = ""; }; 2D2C96C6D5355207398C46C9E66386BE /* RxSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RxSwift-dummy.m"; sourceTree = ""; }; 2D6D95EA842EF7D1BFA6D8191AAE620B /* Repeat.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Repeat.swift; path = RxSwift/Observables/Repeat.swift; sourceTree = ""; }; 2D9BD6B28993FCB68120C1EE96813B72 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2DEC3152A51E8F66A48451B7A4EC9704 /* _RXDelegateProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = _RXDelegateProxy.m; path = RxCocoa/Runtime/_RXDelegateProxy.m; sourceTree = ""; }; 2E647F479FFCF502B2E7DF9A363BE038 /* URLPatchComponentMatchResult.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLPatchComponentMatchResult.swift; path = Sources/URLMatcher/URLPatchComponentMatchResult.swift; sourceTree = ""; }; 2F1C6F4433BDCCAB44D788A1B10A5011 /* TableViewSectionedDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TableViewSectionedDataSource.swift; path = Sources/RxDataSources/TableViewSectionedDataSource.swift; sourceTree = ""; }; 2F8F22838BEB780C855A8033AE7ED2A4 /* UIViewController+TopMostViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIViewController+TopMostViewController.swift"; path = "Sources/URLNavigator/UIViewController+TopMostViewController.swift"; sourceTree = ""; }; 2F954956863F74706AA3A395F8401F14 /* Zip.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Zip.swift; path = RxSwift/Observables/Zip.swift; sourceTree = ""; }; 2F9BB5EBA26D7BF9CA0C3B21B1AAD9EE /* MoyaError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MoyaError.swift; path = Sources/Moya/MoyaError.swift; sourceTree = ""; }; 2FA6CC0C558EDC9E6881C22246E9E059 /* AsSingle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AsSingle.swift; path = RxSwift/Observables/AsSingle.swift; sourceTree = ""; }; 2FD006214C2E7016C83E906BBB4CD624 /* Then.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Then.modulemap; sourceTree = ""; }; 2FF3BD6C6FED1A57B6B88C3F0EFEB6A3 /* FloatingPointType+IdentifiableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "FloatingPointType+IdentifiableType.swift"; path = "Sources/RxDataSources/FloatingPointType+IdentifiableType.swift"; sourceTree = ""; }; 306015976D0F37EFAC711062A258D8E9 /* URLPathComponent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLPathComponent.swift; path = Sources/URLMatcher/URLPathComponent.swift; sourceTree = ""; }; 30C22B352D9B240DC40532BF2241C498 /* SkipUntil.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SkipUntil.swift; path = RxSwift/Observables/SkipUntil.swift; sourceTree = ""; }; 30D68DEE1421B7ED892C4E0B33E9CC35 /* PriorityQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PriorityQueue.swift; path = Platform/DataStructures/PriorityQueue.swift; sourceTree = ""; }; 313319FF55255C3736F46998BCDF7F8A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; 319F0938C83A11D7010E8A6B1A8BC27E /* ReactorKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ReactorKit.xcconfig; sourceTree = ""; }; 328686532CB38406B7BB3A32945B6474 /* ConstraintRelatableTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintRelatableTarget.swift; path = Source/ConstraintRelatableTarget.swift; sourceTree = ""; }; 32D7A430AD912A7D4AEE013B35DE2741 /* NSObject+Rx.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = "NSObject+Rx.modulemap"; sourceTree = ""; }; 332E6B6D83344EA6F35D0681ADEAD1A7 /* Amb.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Amb.swift; path = RxSwift/Observables/Amb.swift; sourceTree = ""; }; 339F05AA6561AB55D443E3626724120A /* RxCocoa.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RxCocoa.h; path = RxCocoa/RxCocoa.h; sourceTree = ""; }; 346F9291AC0BA83FB7B3962FC7F73267 /* PublishRelay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PublishRelay.swift; path = RxCocoa/Traits/PublishRelay.swift; sourceTree = ""; }; 347D8CF191232FE2DBCADBECC8AB9A5F /* Kingfisher-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Kingfisher-dummy.m"; sourceTree = ""; }; 348D24C7C0138AD0B581B484EA58AD8B /* String+MD5.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+MD5.swift"; path = "Sources/String+MD5.swift"; sourceTree = ""; }; 34903204D85860DD93734C3981A9900D /* RxAlamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RxAlamofire-dummy.m"; sourceTree = ""; }; 351865278BBE506A2B088B8755AD46BD /* Sample.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Sample.swift; path = RxSwift/Observables/Sample.swift; sourceTree = ""; }; 351B9B4056B3818966E04C76590FC3B7 /* RxCocoa.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RxCocoa.xcconfig; sourceTree = ""; }; 3565F6A3B549B0D54D7B9937CBDF22F5 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36B939D525A7B01D959ECFCBDE9C0550 /* Kingfisher-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-umbrella.h"; sourceTree = ""; }; 36C41A4EDAFACF2FCF4214E703CD5C47 /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Result/Result.swift; sourceTree = ""; }; 36DC95F95B0777638C99DDB71789CF16 /* DispatchQueueConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DispatchQueueConfiguration.swift; path = RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift; sourceTree = ""; }; 36F99765559218EBDA04ED80AEF34EB9 /* Pods-RxXMLY.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = "Pods-RxXMLY.modulemap"; sourceTree = ""; }; 3771D2D60EF94F8BBA0E912C27A80D24 /* RxTextViewDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTextViewDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift; sourceTree = ""; }; 3795E84704A9198DC6E04FB5A7227CCA /* View.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = View.swift; path = Sources/ReactorKit/View.swift; sourceTree = ""; }; 383DA86E343CBA75A7B8574AB75E1A1B /* RecursiveScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RecursiveScheduler.swift; path = RxSwift/Schedulers/RecursiveScheduler.swift; sourceTree = ""; }; 385C967E6D18858D5311CA87A4205434 /* ReactorKitRuntime.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ReactorKitRuntime.h; path = Sources/ReactorKitRuntime/include/ReactorKitRuntime.h; sourceTree = ""; }; 387296DC1983BFD653D558D678637B29 /* RxSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxSwift-umbrella.h"; sourceTree = ""; }; 38B56F8F2AC8700738ABD303DD4F3606 /* MJRefreshAutoFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoFooter.h; path = MJRefresh/Base/MJRefreshAutoFooter.h; sourceTree = ""; }; 397F2D1B6C0CB8DEFA63186BB12618B2 /* ElementAt.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ElementAt.swift; path = RxSwift/Observables/ElementAt.swift; sourceTree = ""; }; 3A098FD7778F9B758BBC03971A34449C /* SchedulerServices+Emulation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SchedulerServices+Emulation.swift"; path = "RxSwift/Schedulers/SchedulerServices+Emulation.swift"; sourceTree = ""; }; 3B157B779C8184526F0932ECBA9879A9 /* TransformGestureRecognizers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformGestureRecognizers.swift; path = Pod/Classes/iOS/TransformGestureRecognizers.swift; sourceTree = ""; }; 3BB1570E1320634AB029069C7CA04D40 /* Buffer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Buffer.swift; path = RxSwift/Observables/Buffer.swift; sourceTree = ""; }; 3BEAA51E3D065BAC60F1B85CBB1F26D5 /* NSObject+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSObject+Rx.swift"; path = "RxCocoa/Foundation/NSObject+Rx.swift"; sourceTree = ""; }; 3BEF595F5CDFC591D3F78073BECD08C6 /* RxDataSources-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxDataSources-prefix.pch"; sourceTree = ""; }; 3BF627D8C19D8504043EEDE55963F4BA /* UIScrollView+MJRefresh.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+MJRefresh.m"; path = "MJRefresh/UIScrollView+MJRefresh.m"; sourceTree = ""; }; 3CCA6ED6120DA770AB86F564EEAC46DB /* Optional.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Optional.swift; path = RxSwift/Observables/Optional.swift; sourceTree = ""; }; 3D11C85C7F13C6D98A3A465BC90BAD5C /* Utilities.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Utilities.swift; path = Sources/Differentiator/Utilities.swift; sourceTree = ""; }; 3D450DFC0A437B3075E6E68C0FA1A2B3 /* Indicator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Indicator.swift; path = Sources/Indicator.swift; sourceTree = ""; }; 3DA4D4657D80847325EA00819DC11ACD /* DelegateProxyType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DelegateProxyType.swift; path = RxCocoa/Common/DelegateProxyType.swift; sourceTree = ""; }; 3E00F875EA38811C01C6F56B33214C42 /* Moya.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Moya.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3E6B1DB2C5390EA79CEFCA5BAC709AFF /* Generate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Generate.swift; path = RxSwift/Observables/Generate.swift; sourceTree = ""; }; 3E96DBA0FB443524D03EAA0B1907D6DF /* SnapKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = SnapKit.modulemap; sourceTree = ""; }; 3EEF0C4EECF8525D00D4B8CF83ED7D3E /* ObservableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObservableType.swift; path = RxSwift/ObservableType.swift; sourceTree = ""; }; 3F697E60BD60ED1A4604DA7BF8581AB1 /* Pods-RxXMLY.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RxXMLY.release.xcconfig"; sourceTree = ""; }; 3F99F2A0C1CCF46EC645BA9E65F78200 /* IQTitleBarButtonItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQTitleBarButtonItem.swift; path = IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift; sourceTree = ""; }; 404B3E0121644CBA834D592A1EB4E364 /* InfiniteSequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InfiniteSequence.swift; path = Platform/DataStructures/InfiniteSequence.swift; sourceTree = ""; }; 4086B02C434EDF398B3DEAC9194E36E9 /* ConstraintLayoutGuide.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutGuide.swift; path = Source/ConstraintLayoutGuide.swift; sourceTree = ""; }; 40B36A7D5D671C2D2DCB124A4EE87A87 /* MJRefreshAutoNormalFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoNormalFooter.h; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h; sourceTree = ""; }; 40C89EACDC9DB733AEDA2C1858349B05 /* ObjectMapper.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ObjectMapper.xcconfig; sourceTree = ""; }; 41B3ACB613105C496C9AA802C306A439 /* RxGesture.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RxGesture.xcconfig; sourceTree = ""; }; 41DF1FD6C9D2BDC29A48989F838D1ECD /* SectionedViewDataSourceType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SectionedViewDataSourceType.swift; path = RxCocoa/Common/SectionedViewDataSourceType.swift; sourceTree = ""; }; 424B56D99302A846FFD60288ED3225A3 /* ConstraintMaker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMaker.swift; path = Source/ConstraintMaker.swift; sourceTree = ""; }; 4284532690122048902F462ECE7D36F5 /* RxPickerViewDataSourceProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxPickerViewDataSourceProxy.swift; path = RxCocoa/iOS/Proxies/RxPickerViewDataSourceProxy.swift; sourceTree = ""; }; 42A26062E1F6F9B2A5BC508EA3D00288 /* ToJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToJSON.swift; path = Sources/ToJSON.swift; sourceTree = ""; }; 42F0AF2A7F102777266AB6FFCA689615 /* ViewTransition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ViewTransition.swift; path = Sources/RxDataSources/ViewTransition.swift; sourceTree = ""; }; 430B138602CD2D328367A37FEE11E65F /* URLNavigator.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = URLNavigator.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 434BF576D15902B83DA5D893260FA1D6 /* ConstraintLayoutGuideDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutGuideDSL.swift; path = Source/ConstraintLayoutGuideDSL.swift; sourceTree = ""; }; 43F0F183E8964F9C8D16E30F45CF6844 /* MJRefreshBackGifFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackGifFooter.m; path = MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m; sourceTree = ""; }; 44409A050584D330FC1B6F6D3CD9B53E /* MJRefreshAutoStateFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoStateFooter.h; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h; sourceTree = ""; }; 4497D98DB94E983018E6F6C2A4DBAF68 /* Empty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Empty.swift; path = RxSwift/Observables/Empty.swift; sourceTree = ""; }; 44D999B360E6504760FF4A8DD2CB17A1 /* RxMutableBox.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxMutableBox.swift; path = RxSwift/RxMutableBox.swift; sourceTree = ""; }; 459BCD44D36BC348042325F44BD78250 /* ItemPath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ItemPath.swift; path = Sources/Differentiator/ItemPath.swift; sourceTree = ""; }; 4602CBFEB0FDD72D7A2FF806152CFC81 /* RxNavigationControllerDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxNavigationControllerDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxNavigationControllerDelegateProxy.swift; sourceTree = ""; }; 4629139792CEE6CCF32F59002002790D /* RxCocoa.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = RxCocoa.modulemap; sourceTree = ""; }; 465C09D0B89915A2BF9E8EE9623D1080 /* Moya-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Moya-prefix.pch"; sourceTree = ""; }; 46E1355C3CBDC013D697081398F8B462 /* ConstraintLayoutGuide+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ConstraintLayoutGuide+Extensions.swift"; path = "Source/ConstraintLayoutGuide+Extensions.swift"; sourceTree = ""; }; 4761B90E528EAB72D76DD1A76777354E /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Alamofire.modulemap; sourceTree = ""; }; 4773B687AFC9012C6545F9C30E80356F /* UISwitch+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UISwitch+Rx.swift"; path = "RxCocoa/iOS/UISwitch+Rx.swift"; sourceTree = ""; }; 47C651CFF068171CF1C1237A69E7D99A /* Diff.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Diff.swift; path = Sources/Differentiator/Diff.swift; sourceTree = ""; }; 47F3ADD7515A562BFBBBB8888ADA3090 /* HistoricalScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HistoricalScheduler.swift; path = RxSwift/Schedulers/HistoricalScheduler.swift; sourceTree = ""; }; 4896403B4FA72B445E17270A138939D3 /* HasDisposeBag.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HasDisposeBag.swift; sourceTree = ""; }; 4914A9BB07C4CB2A6DDD5D0096A0BE87 /* Reduce.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Reduce.swift; path = RxSwift/Observables/Reduce.swift; sourceTree = ""; }; 49461972A7736ED5C10EA776E946211B /* Disposables.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Disposables.swift; path = RxSwift/Disposables/Disposables.swift; sourceTree = ""; }; 495C0C539FEC03003AFB93904736DE91 /* Cancelable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cancelable.swift; path = RxSwift/Cancelable.swift; sourceTree = ""; }; 49DA791DE895721812C9EB83583324F0 /* URLMatchResult.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLMatchResult.swift; path = Sources/URLMatcher/URLMatchResult.swift; sourceTree = ""; }; 49F4BBE33D9DA445DD902A8A15991A56 /* EnumTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EnumTransform.swift; path = Sources/EnumTransform.swift; sourceTree = ""; }; 4A42C76F3823E2EFA148B18E55C8D6B2 /* BehaviorRelay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BehaviorRelay.swift; path = RxCocoa/Traits/BehaviorRelay.swift; sourceTree = ""; }; 4A6B64332C20EFFA0F9444871E3166E2 /* ConstraintDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDSL.swift; path = Source/ConstraintDSL.swift; sourceTree = ""; }; 4C08FCDD6B32CEDDEA0C67B874CFF29A /* Mappable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Mappable.swift; path = Sources/Mappable.swift; sourceTree = ""; }; 4C13EACC6D3C59A28B692EFB2DB14F6E /* RxCollectionViewDataSourceProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewDataSourceProxy.swift; path = RxCocoa/iOS/Proxies/RxCollectionViewDataSourceProxy.swift; sourceTree = ""; }; 4C467041F0E9683A1B964FB3053EFBD7 /* TYCyclePagerView.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = TYCyclePagerView.modulemap; sourceTree = ""; }; 4CB47246A17CA14228A79384E13C42EC /* CredentialsPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CredentialsPlugin.swift; path = Sources/Moya/Plugins/CredentialsPlugin.swift; sourceTree = ""; }; 4CE22F0C823232387AAE225F52265246 /* BooleanDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BooleanDisposable.swift; path = RxSwift/Disposables/BooleanDisposable.swift; sourceTree = ""; }; 4CF8C44E75B472322D8DC2B86B893ED9 /* SkipWhile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SkipWhile.swift; path = RxSwift/Observables/SkipWhile.swift; sourceTree = ""; }; 4D3E83D571BF2E77162C09F72FD66D3C /* Filter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = Sources/Filter.swift; sourceTree = ""; }; 4D811B382CEAC4FADDA971AFE90AD50A /* CustomDateFormatTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CustomDateFormatTransform.swift; path = Sources/CustomDateFormatTransform.swift; sourceTree = ""; }; 4DBD4CA78A84FE60982AF0780EC62DD2 /* SwiftyJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = Source/SwiftyJSON.swift; sourceTree = ""; }; 4DCA82E849FDD3356D44EC7B8EE2BA84 /* SchedulerType+SharedSequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SchedulerType+SharedSequence.swift"; path = "RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift"; sourceTree = ""; }; 4DD33D08EA36F2C8861A36D8E15B3610 /* SwiftyJSON-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyJSON-dummy.m"; sourceTree = ""; }; 4E295D0CB7D2F465413AC6086CBF8C49 /* SwiftyJSON.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyJSON.xcconfig; sourceTree = ""; }; 4F54F7C7B743ADE4D45AF0D405AA82BA /* ReusableKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReusableKit-prefix.pch"; sourceTree = ""; }; 4F5B061DB45E3912FBD1E11E6F1A842E /* SnapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4F707D328F3FE6B0BA81F88374DC441C /* UIRotationGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIRotationGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UIRotationGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; 4FC00E6B5EA00A0D7CA1DC61742ABA25 /* TYCyclePagerTransformLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYCyclePagerTransformLayout.h; path = TYCyclePagerViewDemo/TYCyclePagerView/TYCyclePagerTransformLayout.h; sourceTree = ""; }; 500A7C34BE698156B2539B1AFD8A1AF3 /* UILongPressGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UILongPressGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UILongPressGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; 502F0D4E9FE8FBC064C04982F146FE43 /* Queue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Queue.swift; path = Platform/DataStructures/Queue.swift; sourceTree = ""; }; 504FC395E9A355EFA723008D25BE2E85 /* URLNavigator-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "URLNavigator-dummy.m"; sourceTree = ""; }; 507ED73099FD654BE7A82BBDAADE2C57 /* IQKeyboardManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardManager.swift; path = IQKeyboardManagerSwift/IQKeyboardManager.swift; sourceTree = ""; }; 50A9B85F6C0935020B51702CEF26AC84 /* TYPagerController.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TYPagerController.xcconfig; sourceTree = ""; }; 50E667F49AB3F41A361BA1B406783ABF /* IQUIView+IQKeyboardToolbar.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIView+IQKeyboardToolbar.swift"; path = "IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift"; sourceTree = ""; }; 51312E298DD4FE8C24D45F0F9149E1EB /* IQKeyboardManagerSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "IQKeyboardManagerSwift-dummy.m"; sourceTree = ""; }; 51BA22372DFDD297D4C486B391AE3C50 /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UICollectionView+Rx.swift"; path = "RxCocoa/iOS/UICollectionView+Rx.swift"; sourceTree = ""; }; 52986B63E04FBCE90E31182FC836DF8C /* Completable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Completable.swift; path = RxSwift/Traits/Completable.swift; sourceTree = ""; }; 52A0824858ABB06E40B1588D14FF9498 /* Constraint.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Constraint.swift; path = Source/Constraint.swift; sourceTree = ""; }; 52A5AC3A3FC014CA05565755B1794774 /* SwiftyColor-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyColor-umbrella.h"; sourceTree = ""; }; 52D36179922B35EB5035CA039865F6B7 /* Image.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.swift; path = Sources/Image.swift; sourceTree = ""; }; 530479F817D071873ED5DC57B4372350 /* HexColorTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HexColorTransform.swift; path = Sources/HexColorTransform.swift; sourceTree = ""; }; 53647AFE918247CC408B6EF13349D614 /* UI+SectionedViewType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UI+SectionedViewType.swift"; path = "Sources/RxDataSources/UI+SectionedViewType.swift"; sourceTree = ""; }; 536B3E455CFA43C1E70A9AD9ACF3CD69 /* UIViewController+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIViewController+Rx.swift"; path = "RxCocoa/iOS/UIViewController+Rx.swift"; sourceTree = ""; }; 5385AD0D889B0E809D292504198A4D49 /* Kingfisher-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-prefix.pch"; sourceTree = ""; }; 53D0CE43D178809120E722B457D98361 /* UISearchController+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UISearchController+Rx.swift"; path = "RxCocoa/iOS/UISearchController+Rx.swift"; sourceTree = ""; }; 53FDE7E458AD9BF6993525EC0EC3CBFF /* SwiftyColor-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyColor-prefix.pch"; sourceTree = ""; }; 541CB7596A9F5575EA8684328858A044 /* Mapper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Mapper.swift; path = Sources/Mapper.swift; sourceTree = ""; }; 542762FC821F584B23860CBBBAF13564 /* AddRef.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AddRef.swift; path = RxSwift/Observables/AddRef.swift; sourceTree = ""; }; 5459AEC7839E1C793C220140241E6CD8 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54D4C9E3C57DE8A35333AAD4C4E744A0 /* SynchronizedDisposeType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SynchronizedDisposeType.swift; path = RxSwift/Concurrency/SynchronizedDisposeType.swift; sourceTree = ""; }; 553E666C8126DEAEA023EE677226741F /* LayoutConstraint.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayoutConstraint.swift; path = Source/LayoutConstraint.swift; sourceTree = ""; }; 553ED5CCD42E1F512E1D9E9466E47D44 /* TakeLast.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TakeLast.swift; path = RxSwift/Observables/TakeLast.swift; sourceTree = ""; }; 55C6E2980720E2A6CC91BB366BC6C7B2 /* MJRefreshNormalHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshNormalHeader.h; path = MJRefresh/Custom/Header/MJRefreshNormalHeader.h; sourceTree = ""; }; 569E77B7F0029A0A2087DE259C07DB22 /* UIPinchGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIPinchGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UIPinchGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; 56FC32B26F57769DE2EFE454C78CC62C /* ImageTransition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageTransition.swift; path = Sources/ImageTransition.swift; sourceTree = ""; }; 57EDA4292816BC724EE611C648E430DA /* Result-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Result-umbrella.h"; sourceTree = ""; }; 58674873A60E033E010D6D00276BA742 /* UIApplication+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIApplication+Rx.swift"; path = "RxCocoa/iOS/UIApplication+Rx.swift"; sourceTree = ""; }; 589D959893A1EF698394AAC806F18758 /* UITapGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITapGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UITapGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; 58D1D78FFF97A04C18826C9383B3DFBD /* RxCocoa-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RxCocoa-dummy.m"; sourceTree = ""; }; 5900BA1E16E8EB96D52F16B3133DD305 /* TakeUntil.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TakeUntil.swift; path = RxSwift/Observables/TakeUntil.swift; sourceTree = ""; }; 591CADBCCC57E88AE178D78B8606C938 /* DataSources.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataSources.swift; path = Sources/RxDataSources/DataSources.swift; sourceTree = ""; }; 59330F519A185D61B0CDC18A2BC8BEF7 /* UISwipeGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UISwipeGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UISwipeGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; 59356F06A577C220AEBEDC900F94102A /* Bag.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Bag.swift; path = Platform/DataStructures/Bag.swift; sourceTree = ""; }; 593B68AD069BBE7957F7B4AE1E9F164A /* InvocableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InvocableType.swift; path = RxSwift/Schedulers/Internal/InvocableType.swift; sourceTree = ""; }; 59830F33473DBCA365D965B7747764A8 /* KingfisherManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherManager.swift; path = Sources/KingfisherManager.swift; sourceTree = ""; }; 59B6B580BF9608405256B589F993BA72 /* BehaviorSubject.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BehaviorSubject.swift; path = RxSwift/Subjects/BehaviorSubject.swift; sourceTree = ""; }; 5A310736267696B70A17786FD9CE22A3 /* NavigatorType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigatorType.swift; path = Sources/URLNavigator/NavigatorType.swift; sourceTree = ""; }; 5B4592368CE0F4DED0B9CAF915FC9215 /* ReusableKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ReusableKit-dummy.m"; sourceTree = ""; }; 5B8B57244416BAE5506E83DFBCCE1F45 /* UIScrollView+MJExtension.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+MJExtension.m"; path = "MJRefresh/UIScrollView+MJExtension.m"; sourceTree = ""; }; 5B95FDEC37C94B6EBD089F1AD6038F94 /* Resource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Resource.swift; path = Sources/Resource.swift; sourceTree = ""; }; 5BCDD91A5C3CBCA65A1E0746F0ABC174 /* Using.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Using.swift; path = RxSwift/Observables/Using.swift; sourceTree = ""; }; 5CB1A48DF38EB6B4A32ADD9DF79B73FF /* ConstraintDescription.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintDescription.swift; path = Source/ConstraintDescription.swift; sourceTree = ""; }; 5D1AFD41323E3922E91E58F1C828C74E /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5D28FC1E29A3FEC5D1CF82FC68A09E26 /* ConstraintConstantTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintConstantTarget.swift; path = Source/ConstraintConstantTarget.swift; sourceTree = ""; }; 5DED5486ED70B440763840FA459F4698 /* TransformOf.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformOf.swift; path = Sources/TransformOf.swift; sourceTree = ""; }; 5DF0A09A6DD2C0453F866A408223B631 /* Reactive.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Reactive.swift; path = RxSwift/Reactive.swift; sourceTree = ""; }; 5E257A033C34CB083326378E455CBA7C /* SnapKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SnapKit-dummy.m"; sourceTree = ""; }; 5E6586D8B7FB36BDE9E96FE450F51E98 /* String+IdentifiableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+IdentifiableType.swift"; path = "Sources/RxDataSources/String+IdentifiableType.swift"; sourceTree = ""; }; 5E7C918776C8129D7372B1095C6E2BBD /* Differentiator-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Differentiator-umbrella.h"; sourceTree = ""; }; 5EAD58DE19CDA807C841C29CE9804B80 /* UITableView+ReusableKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITableView+ReusableKit.swift"; path = "Sources/ReusableKit/UITableView+ReusableKit.swift"; sourceTree = ""; }; 5EEFED166A077BBCCE208D34F71B616C /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = ""; }; 5F34BA700CC37058B6A053DD142E65DB /* ObserveOn.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObserveOn.swift; path = RxSwift/Observables/ObserveOn.swift; sourceTree = ""; }; 5F70D589B9E780A60A3716F46A4ECF0D /* ReactorKitRuntime.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = ReactorKitRuntime.m; path = Sources/ReactorKitRuntime/ReactorKitRuntime.m; sourceTree = ""; }; 6100112F9C1D863256E650CDE2B078D9 /* Kingfisher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Kingfisher.h; path = Sources/Kingfisher.h; sourceTree = ""; }; 610AEB49CFD63698A27742F484C3B01B /* ConstraintInsetTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintInsetTarget.swift; path = Source/ConstraintInsetTarget.swift; sourceTree = ""; }; 610EF28DB806E2D0ECBF17409EB9EABE /* UINavigationItem+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UINavigationItem+Rx.swift"; path = "RxCocoa/iOS/UINavigationItem+Rx.swift"; sourceTree = ""; }; 613B23B6CFFCB99006B5893BE2C653A5 /* MJRefreshStateHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshStateHeader.m; path = MJRefresh/Custom/Header/MJRefreshStateHeader.m; sourceTree = ""; }; 622801585769EF5EF95DF31C3FC639A9 /* Signal+Subscription.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Signal+Subscription.swift"; path = "RxCocoa/Traits/Signal/Signal+Subscription.swift"; sourceTree = ""; }; 62DDFD5098F37CBDB331E2282F8DEB42 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63C44514B1EC24A99AC7728B3503A038 /* Single.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Single.swift; path = RxSwift/Traits/Single.swift; sourceTree = ""; }; 63CA5A419783AFF7463EA6144B0C1E36 /* IntegerType+IdentifiableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IntegerType+IdentifiableType.swift"; path = "Sources/RxDataSources/IntegerType+IdentifiableType.swift"; sourceTree = ""; }; 63D809CF6C0676EAD17D2011895CB209 /* URLNavigator.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = URLNavigator.modulemap; sourceTree = ""; }; 63F72D0F593F82201EA482E15012B694 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObservableConvertibleType.swift; path = RxSwift/ObservableConvertibleType.swift; sourceTree = ""; }; 645CC45F2863AE7D2A6FB2A8C23E620F /* UISegmentedControl+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UISegmentedControl+Rx.swift"; path = "RxCocoa/iOS/UISegmentedControl+Rx.swift"; sourceTree = ""; }; 64E69BA43FC5913EAFEEADEA145AFF1F /* RxCocoaRuntime.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RxCocoaRuntime.h; path = RxCocoa/Runtime/include/RxCocoaRuntime.h; sourceTree = ""; }; 64ED938103FC90F798F7A60F04981575 /* UIGestureRecognizer+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIGestureRecognizer+Rx.swift"; path = "RxCocoa/iOS/UIGestureRecognizer+Rx.swift"; sourceTree = ""; }; 655359FE97777A5E6376898C32918AD1 /* DateFormatterTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateFormatterTransform.swift; path = Sources/DateFormatterTransform.swift; sourceTree = ""; }; 65CB9B0C5FAEB1D7382D6EDFA4938D81 /* Differentiator-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Differentiator-prefix.pch"; sourceTree = ""; }; 66AD8E23D6C4A5FFCFE4C066E700B676 /* VirtualTimeConverterType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = VirtualTimeConverterType.swift; path = RxSwift/Schedulers/VirtualTimeConverterType.swift; sourceTree = ""; }; 66B23F5CD12DC047D454DFB36DAF64B7 /* AnyEncodable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyEncodable.swift; path = Sources/Moya/AnyEncodable.swift; sourceTree = ""; }; 66C7719FC6A1EB8A6E7F08B0215685B3 /* RecursiveLock.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RecursiveLock.swift; path = Platform/RecursiveLock.swift; sourceTree = ""; }; 68051FD77C0407C9B24515E31E177222 /* ObjectMapper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ObjectMapper.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68668B40B5E550006A59D5621EFB0561 /* ReusableKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReusableKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6A8E64555EB2FE7D579467AABA52EB26 /* ResultProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResultProtocol.swift; path = Result/ResultProtocol.swift; sourceTree = ""; }; 6AC97A632E3388162A1E604E64EF8D3C /* URLNavigator-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "URLNavigator-prefix.pch"; sourceTree = ""; }; 6B2C9F6C4EE9F9989261CF1F3418FC9E /* RxCollectionViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewReactiveArrayDataSource.swift; path = RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift; sourceTree = ""; }; 6C6A3B044CC344E98ACD7182BCE0685D /* TYPagerController.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = TYPagerController.modulemap; sourceTree = ""; }; 6C82E28899508F47FDD43F9C7D4722F0 /* TYPagerView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYPagerView.m; path = TYPagerControllerDemo/TYPagerController/TYPagerView.m; sourceTree = ""; }; 6D4BA91A34AD8C308124A2BAC574B3B9 /* MJRefreshBackFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackFooter.m; path = MJRefresh/Base/MJRefreshBackFooter.m; sourceTree = ""; }; 6D94ED6DD481751BB6F57C5B6C60049A /* IQUIWindow+Hierarchy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIWindow+Hierarchy.swift"; path = "IQKeyboardManagerSwift/Categories/IQUIWindow+Hierarchy.swift"; sourceTree = ""; }; 6DB87787AD0F7333249395D1378FED63 /* AnonymousObserver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnonymousObserver.swift; path = RxSwift/Observers/AnonymousObserver.swift; sourceTree = ""; }; 6E3539D710126BA8CE5B7B6A9EBF994E /* NetworkLoggerPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkLoggerPlugin.swift; path = Sources/Moya/Plugins/NetworkLoggerPlugin.swift; sourceTree = ""; }; 6E8E6375D3923532919D7663A2689D73 /* AnimationConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimationConfiguration.swift; path = Sources/RxDataSources/AnimationConfiguration.swift; sourceTree = ""; }; 6EADA7DDE939B839BF4574A8A38504C9 /* ReactorKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ReactorKit-dummy.m"; sourceTree = ""; }; 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 6F6066636CC572B64FC9681E44E91280 /* Result.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Result.modulemap; sourceTree = ""; }; 6FADD7C8C693834E93343EFB77D33AAD /* _RX.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = _RX.m; path = RxCocoa/Runtime/_RX.m; sourceTree = ""; }; 6FE766E158FAC90198034E79F61FB2CE /* RxPickerViewAdapter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxPickerViewAdapter.swift; path = RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift; sourceTree = ""; }; 701196C1B8D5354D685D7DD627877FEA /* TailRecursiveSink.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TailRecursiveSink.swift; path = RxSwift/Observers/TailRecursiveSink.swift; sourceTree = ""; }; 70459A28397D1E419B4B3394FA8435F3 /* Cancellable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cancellable.swift; path = Sources/Moya/Cancellable.swift; sourceTree = ""; }; 705EED60B52573B696D1FD8B6CB52FC2 /* ConstraintLayoutSupportDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutSupportDSL.swift; path = Source/ConstraintLayoutSupportDSL.swift; sourceTree = ""; }; 70876246376D5A1D2C4ADF75F307186A /* OperationQueueScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OperationQueueScheduler.swift; path = RxSwift/Schedulers/OperationQueueScheduler.swift; sourceTree = ""; }; 7088D6C1ADBF5BBD12CFF84A0E220FA2 /* UITextField+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITextField+Rx.swift"; path = "RxCocoa/iOS/UITextField+Rx.swift"; sourceTree = ""; }; 70EBA339974E545CDC23F472B6A4BD3E /* SingleAssignmentDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SingleAssignmentDisposable.swift; path = RxSwift/Disposables/SingleAssignmentDisposable.swift; sourceTree = ""; }; 712CA70976070E14072608793D09319A /* SwiftyJSON.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = SwiftyJSON.modulemap; sourceTree = ""; }; 716F007B040C0CDD77F14CF5209FEE01 /* Pods-RxXMLY.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RxXMLY.debug.xcconfig"; sourceTree = ""; }; 720B12F497653C86DA2AF27BCCDEFC2D /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = ""; }; 7253973D128A903FF19D65F0A9DB9316 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = ""; }; 737DA5252745C9E3883929C8A145E22E /* Moya-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Moya-umbrella.h"; sourceTree = ""; }; 74418F5529BCA8630BE486D0F6A76D6C /* TTRangeSlider.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = TTRangeSlider.modulemap; sourceTree = ""; }; 74515FDED92421AF135AF3C0B8B08A1B /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; 7481193578343285EC8F430B736D1314 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; 74A0AC039E269E338928C0D25F4ABC01 /* Endpoint.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Endpoint.swift; path = Sources/Moya/Endpoint.swift; sourceTree = ""; }; 74AE307751AFEBCC18F1E034FE08A05F /* Box.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Box.swift; path = Sources/Box.swift; sourceTree = ""; }; 7553CCC7443EE20DAEB595357CD7198F /* RxTableViewDataSourceProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTableViewDataSourceProxy.swift; path = RxCocoa/iOS/Proxies/RxTableViewDataSourceProxy.swift; sourceTree = ""; }; 75AD8F0F482E2F585B2664D1635B4706 /* MJRefreshConst.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshConst.m; path = MJRefresh/MJRefreshConst.m; sourceTree = ""; }; 7645EEB7C195C4CF487DFA28BA57D8E5 /* UIRefreshControl+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIRefreshControl+Rx.swift"; path = "RxCocoa/iOS/UIRefreshControl+Rx.swift"; sourceTree = ""; }; 76B7D152E72F3D0808E4186445E4FC9B /* IQNSArray+Sort.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQNSArray+Sort.swift"; path = "IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift"; sourceTree = ""; }; 76C763EC2CA9D13E7E93A029686393D1 /* CacheSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CacheSerializer.swift; path = Sources/CacheSerializer.swift; sourceTree = ""; }; 770CC8138D39A339E5C8057EC2DD1F7D /* DelaySubscription.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DelaySubscription.swift; path = RxSwift/Observables/DelaySubscription.swift; sourceTree = ""; }; 77AB7A6BBF44C678FD2D852835009017 /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77D21DAC0D06E0D6BBF5A78CB767E815 /* MJRefreshNormalHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshNormalHeader.m; path = MJRefresh/Custom/Header/MJRefreshNormalHeader.m; sourceTree = ""; }; 78266012E1D0050485CA537DD6F66FF5 /* MJRefresh-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MJRefresh-umbrella.h"; sourceTree = ""; }; 7897B0DA6B24D6557C87D44F527163A2 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewSectionedReloadDataSource.swift; path = Sources/RxDataSources/RxCollectionViewSectionedReloadDataSource.swift; sourceTree = ""; }; 78C07D23AF9D0ED48A9FEB791BA9FE25 /* DictionaryTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DictionaryTransform.swift; path = Sources/DictionaryTransform.swift; sourceTree = ""; }; 79B73C8686EF3B5668892169B223FA54 /* ReusableKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = ReusableKit.modulemap; sourceTree = ""; }; 7A0ECA86834476786492EC1E52EB3E13 /* GroupBy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupBy.swift; path = RxSwift/Observables/GroupBy.swift; sourceTree = ""; }; 7A4923E32C7795B1406F85C0D769B8F1 /* NavigatorDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigatorDelegate.swift; path = Sources/URLNavigator/NavigatorDelegate.swift; sourceTree = ""; }; 7A8F833CA4526A1FF65CC394962AD92E /* NSObject+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx.swift"; sourceTree = ""; }; 7AA6F3E86B6FCC86A1E5A52F243EE56D /* SwiftyColor.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyColor.xcconfig; sourceTree = ""; }; 7ADE1D2FF0526260DCE037584DAB5A0D /* MapError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MapError.swift; path = Sources/MapError.swift; sourceTree = ""; }; 7B3743F6F50232B4A27328B734F963F8 /* UITableView+RxReusableKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITableView+RxReusableKit.swift"; path = "Sources/RxReusableKit/UITableView+RxReusableKit.swift"; sourceTree = ""; }; 7B37D88DF834AFA3231D9F6EF76598D8 /* ConstraintPriorityTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintPriorityTarget.swift; path = Source/ConstraintPriorityTarget.swift; sourceTree = ""; }; 7BED0C58934B33DC41A1432BABD2EFA8 /* TakeWhile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TakeWhile.swift; path = RxSwift/Observables/TakeWhile.swift; sourceTree = ""; }; 7C1A7F6D1EC5B670FE3CF2E00D21C2D6 /* Driver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Driver.swift; path = RxCocoa/Traits/Driver/Driver.swift; sourceTree = ""; }; 7C4B50F1957DB069477D1DF3B46EE040 /* UIProgressView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIProgressView+Rx.swift"; path = "RxCocoa/iOS/UIProgressView+Rx.swift"; sourceTree = ""; }; 7C5ABD7D168407FE39CC76E2B5D13784 /* Scan.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Scan.swift; path = RxSwift/Observables/Scan.swift; sourceTree = ""; }; 7D4430407B638D271F51E5D3E920BB51 /* Deferred.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Deferred.swift; path = RxSwift/Observables/Deferred.swift; sourceTree = ""; }; 7DA9FEB5DF32D81B067986FD382163B1 /* SwiftyColor-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyColor-dummy.m"; sourceTree = ""; }; 7E01EF7A9628B4C49AC12163B4FE6A91 /* UIControl+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIControl+Rx.swift"; path = "RxCocoa/iOS/UIControl+Rx.swift"; sourceTree = ""; }; 7E2E1626B3D838551A7744F976FEB850 /* ConstraintOffsetTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintOffsetTarget.swift; path = Source/ConstraintOffsetTarget.swift; sourceTree = ""; }; 7FF0A342E014C442F7C7474857AF5961 /* Timeline.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timeline.swift; path = Source/Timeline.swift; sourceTree = ""; }; 8065BEF90C7FDDBD3D7FB5E732D08AD7 /* RxGesture.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = RxGesture.modulemap; sourceTree = ""; }; 80810E76C51AA5F19CE1D4B0D55E060C /* IQUITextFieldView+Additions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUITextFieldView+Additions.swift"; path = "IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift"; sourceTree = ""; }; 809E8BEC46CF11685F9980428C93B446 /* RxCocoaObjCRuntimeError+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "RxCocoaObjCRuntimeError+Extensions.swift"; path = "RxCocoa/Common/RxCocoaObjCRuntimeError+Extensions.swift"; sourceTree = ""; }; 80A58779165736F60C355BBAAF75C027 /* Errors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Errors.swift; path = RxSwift/Errors.swift; sourceTree = ""; }; 80DD88365EFE5E0A8E363300ADCE2DCC /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = ""; }; 812C7F916BBBCD2970C245989B39CD1B /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Source/Result.swift; sourceTree = ""; }; 81B1BE2330946471DDB4A3A11D43E0B8 /* ConstraintView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintView.swift; path = Source/ConstraintView.swift; sourceTree = ""; }; 81B6FFA1A029CC85A9BD8A4847631CD7 /* IntegerOperators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IntegerOperators.swift; path = Sources/IntegerOperators.swift; sourceTree = ""; }; 82C7B1F1647EBCD9A81E7A2788701631 /* SnapKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SnapKit.xcconfig; sourceTree = ""; }; 834769F9B06E2DF4306C35CB6F967BAE /* ObserverType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObserverType.swift; path = RxSwift/ObserverType.swift; sourceTree = ""; }; 83A11C513330652C7580864E6FF559BC /* AssociatedObjectStore.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssociatedObjectStore.swift; path = Sources/ReactorKit/AssociatedObjectStore.swift; sourceTree = ""; }; 83B89463432FAA55975050E17F0E6B42 /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustPolicy.swift; path = Source/ServerTrustPolicy.swift; sourceTree = ""; }; 83BF88F0362AAB340061528783A19B27 /* AnimatableSectionModelType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatableSectionModelType.swift; path = Sources/Differentiator/AnimatableSectionModelType.swift; sourceTree = ""; }; 83F0BF62BA5033111F22B49D2DA78D30 /* ConstraintMakerFinalizable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerFinalizable.swift; path = Source/ConstraintMakerFinalizable.swift; sourceTree = ""; }; 84378AB9BAB98B7273235CFA0707DE2D /* RxPickerViewAdapter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxPickerViewAdapter.swift; path = Sources/RxDataSources/RxPickerViewAdapter.swift; sourceTree = ""; }; 84E9E4B67AD852A6167EFE006AD2043F /* NSObject_Rx.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NSObject_Rx.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8503DD41469B8F78519D7D0B98DBEE66 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 85302FAB5FE773C188E3758D45ABFF83 /* Pods-RxXMLY-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-RxXMLY-dummy.m"; sourceTree = ""; }; 8533AEEE8CD59BA624CA9267423D28E5 /* NSObject+Rx-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "NSObject+Rx-umbrella.h"; sourceTree = ""; }; 8546B56E228E6A78E32833D60BB6D648 /* UIView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIView+Rx.swift"; path = "RxCocoa/iOS/UIView+Rx.swift"; sourceTree = ""; }; 85B5D2C3BA32F9BC3AA7C975B97F6A50 /* CollectionViewSectionedDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CollectionViewSectionedDataSource.swift; path = Sources/RxDataSources/CollectionViewSectionedDataSource.swift; sourceTree = ""; }; 861BD4AC5D5011049786E6352C62DC93 /* StartWith.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StartWith.swift; path = RxSwift/Observables/StartWith.swift; sourceTree = ""; }; 86FCD05DCA4B5988057772E478AA3B7C /* BehaviorRelay+Driver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BehaviorRelay+Driver.swift"; path = "RxCocoa/Traits/Driver/BehaviorRelay+Driver.swift"; sourceTree = ""; }; 87874701D9E81D433E42E8AAABDFFD7A /* IQTextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQTextView.swift; path = IQKeyboardManagerSwift/IQTextView/IQTextView.swift; sourceTree = ""; }; 87C5FAB0B3B58A21449A74F3BE0722D4 /* GestureFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GestureFactory.swift; path = Pod/Classes/GestureFactory.swift; sourceTree = ""; }; 87CE09E0A8341C183F59A37440D5153A /* InvocableScheduledItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InvocableScheduledItem.swift; path = RxSwift/Schedulers/Internal/InvocableScheduledItem.swift; sourceTree = ""; }; 88860B4DEDC2F32A5AC3E06DD5F534B0 /* TYTabPagerController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYTabPagerController.h; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerController.h; sourceTree = ""; }; 889059818A6183D31A0BDC7C6B981849 /* ControlProperty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ControlProperty.swift; path = RxCocoa/Traits/ControlProperty.swift; sourceTree = ""; }; 88CB6A2CAB2E3799803CD1C118F1F107 /* IQUIViewController+Additions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIViewController+Additions.swift"; path = "IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift"; sourceTree = ""; }; 88CD1219127C85AAD3C013FB79D9BA84 /* ImmutableMappable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImmutableMappable.swift; path = Sources/ImmutableMappable.swift; sourceTree = ""; }; 89BBA936343137BAF519218BA7905C65 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8A5ABA18A2D1A61A185ED12DF70B8F30 /* Result.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Result.xcconfig; sourceTree = ""; }; 8ACB6BCA51D81D396B134C596B0FB8C1 /* Dematerialize.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Dematerialize.swift; path = RxSwift/Observables/Dematerialize.swift; sourceTree = ""; }; 8B3B388CB20702989EAED439666B6E88 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8BCCB28DE00E01DFCDBAFD966F967237 /* Catch.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Catch.swift; path = RxSwift/Observables/Catch.swift; sourceTree = ""; }; 8C5BD7D77FAF305B2C6E8F85C768FC42 /* RefCountDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RefCountDisposable.swift; path = RxSwift/Disposables/RefCountDisposable.swift; sourceTree = ""; }; 8C6994BF34E50D97EB111CDFB02DA22A /* Moya-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Moya-dummy.m"; sourceTree = ""; }; 8C8ADE73FE10FA15ABAA1CB4FB6DAD62 /* Maybe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Maybe.swift; path = RxSwift/Traits/Maybe.swift; sourceTree = ""; }; 8CF3671CDFCBADF26426E2EC9ED34424 /* RxTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTarget.swift; path = RxCocoa/Common/RxTarget.swift; sourceTree = ""; }; 8D3F2B09E35E2D2EFA58CB9D9261E7D0 /* NSObject+Rx.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "NSObject+Rx.xcconfig"; sourceTree = ""; }; 8D9FE6C7F2AC2C3319C58D4272FD52C1 /* IQKeyboardManagerSwift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = IQKeyboardManagerSwift.xcconfig; sourceTree = ""; }; 8DC2D7127D27BDD70C5EC0A4FF96074D /* Producer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Producer.swift; path = RxSwift/Observables/Producer.swift; sourceTree = ""; }; 8E365162D0EA4A1CE302074F5079D99D /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ObservableConvertibleType+Driver.swift"; path = "RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; 8E49B1A15261BE86B3137451B245C6BE /* TYTabPagerBar.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYTabPagerBar.m; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerBar.m; sourceTree = ""; }; 8E6990B20E9EA105BBB88B21C57593B5 /* IQBarButtonItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQBarButtonItem.swift; path = IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift; sourceTree = ""; }; 8F2CEBF6D286D3D808C8AB81A58AFF59 /* Debug.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Debug.swift; path = RxSwift/Observables/Debug.swift; sourceTree = ""; }; 8F3B9C1AE8FD54707F43CF28561D8FF7 /* SwiftyJSON-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-prefix.pch"; sourceTree = ""; }; 8F5E590482B741DD275B9A87B7EAF4DA /* Never.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Never.swift; path = RxSwift/Observables/Never.swift; sourceTree = ""; }; 8F92102426225DFFC6D512690BD4167B /* ConstraintMakerPriortizable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMakerPriortizable.swift; path = Source/ConstraintMakerPriortizable.swift; sourceTree = ""; }; 8FF79B88AE1A71885D5E17758DB372B4 /* MJRefresh-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MJRefresh-prefix.pch"; sourceTree = ""; }; 8FF93E1E76B5ED1DA01941D734DB3531 /* MJRefreshFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshFooter.h; path = MJRefresh/Base/MJRefreshFooter.h; sourceTree = ""; }; 90107D958C65D510896483F2CB8060BA /* ObservableType+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ObservableType+Extensions.swift"; path = "RxSwift/ObservableType+Extensions.swift"; sourceTree = ""; }; 9096DCC6CC66370C372F38BF8C1E435A /* RxPickerViewDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxPickerViewDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxPickerViewDelegateProxy.swift; sourceTree = ""; }; 90D0F930F0AE0A2471269302003B8A5F /* RxWebViewDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxWebViewDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxWebViewDelegateProxy.swift; sourceTree = ""; }; 91425961AD55DFFE4C3BCB82A387076E /* Differentiator.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Differentiator.modulemap; sourceTree = ""; }; 9158E32ACD5A2977E6DE91E13F5C7A66 /* TYPagerController-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TYPagerController-prefix.pch"; sourceTree = ""; }; 91BE41B89A55956B97FA90BA78DA1A4C /* MJRefreshHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshHeader.h; path = MJRefresh/Base/MJRefreshHeader.h; sourceTree = ""; }; 91EFB17C939EB1CE9FDE66074D20AA83 /* MJRefreshComponent.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshComponent.h; path = MJRefresh/Base/MJRefreshComponent.h; sourceTree = ""; }; 91F4320128E8926FF21C0A92F70CFBBD /* TYCyclePagerView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TYCyclePagerView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 923B40C1B730134DE3FB93D56434F359 /* SectionModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SectionModel.swift; path = Sources/Differentiator/SectionModel.swift; sourceTree = ""; }; 924F8DE96A22311CC14038BB62C8997D /* IQKeyboardManagerSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IQKeyboardManagerSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 9547423EEA761582EF5FDB90684A1EE8 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 954CE4894A117D9DA14AE5D47A6BAE88 /* Map.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Map.swift; path = RxSwift/Observables/Map.swift; sourceTree = ""; }; 957D3B23678E4698CC79A2DE9EA9F96A /* _RXKVOObserver.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = _RXKVOObserver.m; path = RxCocoa/Runtime/_RXKVOObserver.m; sourceTree = ""; }; 95A5AC992E4B703AA08D0A815AAED779 /* ImmediateSchedulerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImmediateSchedulerType.swift; path = RxSwift/ImmediateSchedulerType.swift; sourceTree = ""; }; 95CF89A127DC79551BAA8988F82BD1E2 /* Event.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Event.swift; path = RxSwift/Event.swift; sourceTree = ""; }; 95E7A514BFE979A1185DE2AD4CF665AB /* IQKeyboardManagerSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "IQKeyboardManagerSwift-prefix.pch"; sourceTree = ""; }; 95F766DA63DDEB880E468218435A871E /* SessionManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionManager.swift; path = Source/SessionManager.swift; sourceTree = ""; }; 9647D7ACC10E74465531ADEEFD4D2E0B /* ActionSubject.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ActionSubject.swift; path = Sources/ReactorKit/ActionSubject.swift; sourceTree = ""; }; 96BC1A1EF9084AA9223F0E5E094C6ABC /* DispatchQueue+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Extensions.swift"; path = "Platform/DispatchQueue+Extensions.swift"; sourceTree = ""; }; 96BF47DDEF17AC72F8EBF9CF0EA9DC04 /* _RXObjCRuntime.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = _RXObjCRuntime.m; path = RxCocoa/Runtime/_RXObjCRuntime.m; sourceTree = ""; }; 9707233113E3D0E414A32F652E88D1D8 /* UITabBar+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITabBar+Rx.swift"; path = "RxCocoa/iOS/UITabBar+Rx.swift"; sourceTree = ""; }; 973B74E176AD01454BE984AC4A7136A0 /* IQKeyboardManagerSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "IQKeyboardManagerSwift-umbrella.h"; sourceTree = ""; }; 97927D56FAC9E735B7340A75B06C0600 /* Then.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Then.swift; path = Sources/Then/Then.swift; sourceTree = ""; }; 97B60E643384171A74CED9D708E98F3D /* RxDataSources.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxDataSources.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9855A9C6CDDA35414C161F75BED8B6F8 /* Alamofire.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.xcconfig; sourceTree = ""; }; 98B0262ADD4A92D4CCFE4F51BE3FD5D5 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIBarButtonItem+Rx.swift"; path = "RxCocoa/iOS/UIBarButtonItem+Rx.swift"; sourceTree = ""; }; 98BFA9BCF11C38855F14FC8D710D4E95 /* RxCocoa.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCocoa.swift; path = RxCocoa/RxCocoa.swift; sourceTree = ""; }; 9946E97DCFB4572476564D33A05975E4 /* SynchronizedUnsubscribeType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SynchronizedUnsubscribeType.swift; path = RxSwift/Concurrency/SynchronizedUnsubscribeType.swift; sourceTree = ""; }; 99692BFA25CF2437019950A2EFAE0291 /* VirtualTimeScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = VirtualTimeScheduler.swift; path = RxSwift/Schedulers/VirtualTimeScheduler.swift; sourceTree = ""; }; 9979EBBDCDD9A7DDDB0725FEC97660E7 /* URLTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLTransform.swift; path = Sources/URLTransform.swift; sourceTree = ""; }; 99F328B2BEE043C872339B6E419D2095 /* Sequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Sequence.swift; path = RxSwift/Observables/Sequence.swift; sourceTree = ""; }; 9A82E326004C5937DB3D98AD29CEDFBA /* Platform.Linux.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Platform.Linux.swift; path = Platform/Platform.Linux.swift; sourceTree = ""; }; 9BBEE2DB22AD2B01DE2D742F08B660DE /* UIButton+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIButton+Rx.swift"; path = "RxCocoa/iOS/UIButton+Rx.swift"; sourceTree = ""; }; 9C5AF229FF21FF8786BD3CDF9E0BD504 /* Platform.Linux.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Platform.Linux.swift; path = Platform/Platform.Linux.swift; sourceTree = ""; }; 9CD9A0FE14A09A30BF80B2ABA5E19488 /* RxGestureRecognizerDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxGestureRecognizerDelegate.swift; path = Pod/Classes/RxGestureRecognizerDelegate.swift; sourceTree = ""; }; 9CEA02DF9281493AB02811A381B532ED /* TYCyclePagerView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TYCyclePagerView.xcconfig; sourceTree = ""; }; 9D01082F46B5E949B212F6A27CDEF6DF /* RxTabBarControllerDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTabBarControllerDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxTabBarControllerDelegateProxy.swift; sourceTree = ""; }; 9D4AFB0C0C4C623DA01CF63FA53AE13C /* RxSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = RxSwift.modulemap; sourceTree = ""; }; 9D52DB027375B4BF6987825A6BF5751B /* RxAlamofire.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RxAlamofire.xcconfig; sourceTree = ""; }; 9D8CA91B2544EA3008663944FBDDBB5F /* MJRefreshGifHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshGifHeader.h; path = MJRefresh/Custom/Header/MJRefreshGifHeader.h; sourceTree = ""; }; 9DB0EFCE41B9CC2A562FFA3E9B2C8CD1 /* TTRangeSlider-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TTRangeSlider-prefix.pch"; sourceTree = ""; }; 9F86ADA13F9525995BA4102D3E1950B2 /* RxCollectionViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewSectionedAnimatedDataSource.swift; path = Sources/RxDataSources/RxCollectionViewSectionedAnimatedDataSource.swift; sourceTree = ""; }; 9FDFBC7B8CD4E93779C4C76ACF38B06D /* FormatIndicatedCacheSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FormatIndicatedCacheSerializer.swift; path = Sources/FormatIndicatedCacheSerializer.swift; sourceTree = ""; }; A088E5F2C74178B8F5D416C973214422 /* Zip+Collection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Zip+Collection.swift"; path = "RxSwift/Observables/Zip+Collection.swift"; sourceTree = ""; }; A0E83A69D9C8F0B40317DD90BF832D26 /* SwitchIfEmpty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwitchIfEmpty.swift; path = RxSwift/Observables/SwitchIfEmpty.swift; sourceTree = ""; }; A1A112A635DA48EF8CB77AF4F3AE614D /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A212CBEA9196364FF0A6F2FA43D9CE76 /* ReactorKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = ReactorKit.modulemap; sourceTree = ""; }; A222328DE93D544E711805351EB49909 /* RxDataSources.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RxDataSources.xcconfig; sourceTree = ""; }; A25F9545112FB4EA2545D303AC81CFB1 /* Delay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Delay.swift; path = RxSwift/Observables/Delay.swift; sourceTree = ""; }; A276DD5300A0CD1F004D67179921FE96 /* AsyncLock.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AsyncLock.swift; path = RxSwift/Concurrency/AsyncLock.swift; sourceTree = ""; }; A27C7B9B2AA2EBC3F3078495A3B4B248 /* TTRangeSlider-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "TTRangeSlider-dummy.m"; sourceTree = ""; }; A2B78D4FFAD19F5D58FA9B42B6E4F2FE /* NSLayoutConstraint+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSLayoutConstraint+Rx.swift"; path = "RxCocoa/Common/NSLayoutConstraint+Rx.swift"; sourceTree = ""; }; A32996D04BF42DF4610DB92306B85695 /* AnimatableSectionModelType+ItemPath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "AnimatableSectionModelType+ItemPath.swift"; path = "Sources/Differentiator/AnimatableSectionModelType+ItemPath.swift"; sourceTree = ""; }; A468830C4A1700A0C17545C662543173 /* ConstraintViewDSL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintViewDSL.swift; path = Source/ConstraintViewDSL.swift; sourceTree = ""; }; A46A36F4BF38056EAA48578282F54B60 /* TTRangeSlider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TTRangeSlider.m; path = Pod/Classes/TTRangeSlider.m; sourceTree = ""; }; A5C87EF44982A4D3803D88EB9983E52D /* UIGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UIGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; A5D6508C06EC007A57DBB3099AD5C617 /* LockOwnerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LockOwnerType.swift; path = RxSwift/Concurrency/LockOwnerType.swift; sourceTree = ""; }; A5E925D1347A4A7AE44D07D3342D1BCB /* TYCyclePagerView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TYCyclePagerView-umbrella.h"; sourceTree = ""; }; A678D15F6F0F97A723B186CEA25EE5A4 /* Zip+arity.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Zip+arity.swift"; path = "RxSwift/Observables/Zip+arity.swift"; sourceTree = ""; }; A6BE7CE8E3CF10B0BE4713CA7CD3FDC9 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A6EC870FE5CE9FD966D23435C1DB6E0C /* IQKeyboardReturnKeyHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardReturnKeyHandler.swift; path = IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift; sourceTree = ""; }; A6F0EAB300D0CE91C61EF3E29868A1D0 /* NSObject+Rx-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "NSObject+Rx-prefix.pch"; sourceTree = ""; }; A7F044D3460E87057EB82CCDD5C4D545 /* Differentiator.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Differentiator.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A7F1EA6B598A844B871DB54C2C1C197D /* AnimatableSectionModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatableSectionModel.swift; path = Sources/Differentiator/AnimatableSectionModel.swift; sourceTree = ""; }; A82133367E269E7AA71B9DB6052EFEEB /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; A93A51F7564EC64C94864C9A11DF7A0D /* TYPageControl.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYPageControl.h; path = TYCyclePagerViewDemo/TYCyclePagerView/TYPageControl.h; sourceTree = ""; }; A93AB57B4326570AC0951E305DD04B1F /* RxAlamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxAlamofire-umbrella.h"; sourceTree = ""; }; A95B5537CC319932AD399F0660CB16D8 /* SwiftyColor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyColor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A962B38EA206CD0B32DEC9E6DC414479 /* ControlEvent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ControlEvent.swift; path = RxCocoa/Traits/ControlEvent.swift; sourceTree = ""; }; A9A3DB0E144693D175CBA841C8B10601 /* RetryWhen.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RetryWhen.swift; path = RxSwift/Observables/RetryWhen.swift; sourceTree = ""; }; AA64CC4D23BCB33D713381874C6A93C9 /* RxTableViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTableViewSectionedReloadDataSource.swift; path = Sources/RxDataSources/RxTableViewSectionedReloadDataSource.swift; sourceTree = ""; }; AA67591D1C61133E2F626EA1C2E097FE /* Observable+Bind.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Observable+Bind.swift"; path = "RxCocoa/Common/Observable+Bind.swift"; sourceTree = ""; }; AAA5BE3862DD3A00CBB848A024A5E863 /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ControlEvent+Driver.swift"; path = "RxCocoa/Traits/Driver/ControlEvent+Driver.swift"; sourceTree = ""; }; AB53371318D8626F8F670FACB15EC0F0 /* MoyaProvider+Internal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "MoyaProvider+Internal.swift"; path = "Sources/Moya/MoyaProvider+Internal.swift"; sourceTree = ""; }; ABBD78A9569D4426E15EAE838A7F4562 /* RxSearchControllerDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxSearchControllerDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxSearchControllerDelegateProxy.swift; sourceTree = ""; }; ABF289602106E94D3F277B9C7F39FF7C /* UIView+MJExtension.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MJExtension.m"; path = "MJRefresh/UIView+MJExtension.m"; sourceTree = ""; }; AC1DC57990281D4F45BE128AADCE4143 /* UICollectionView+ReusableKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UICollectionView+ReusableKit.swift"; path = "Sources/ReusableKit/UICollectionView+ReusableKit.swift"; sourceTree = ""; }; AC294161F7CCD1280208FEC5EE254218 /* Driver+Subscription.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Driver+Subscription.swift"; path = "RxCocoa/Traits/Driver/Driver+Subscription.swift"; sourceTree = ""; }; AE4600B7894931DEAFCABE02CD7AB268 /* GroupedObservable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupedObservable.swift; path = RxSwift/GroupedObservable.swift; sourceTree = ""; }; AE48FA13FBA4585E33723ED2572A6754 /* TextInput.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextInput.swift; path = RxCocoa/Common/TextInput.swift; sourceTree = ""; }; AE7697FE2AC8BF4148D6676FBC1B5A23 /* UIWebView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIWebView+Rx.swift"; path = "RxCocoa/iOS/UIWebView+Rx.swift"; sourceTree = ""; }; AE8EA095AFC8941AABB28F7469B54C94 /* Pods-RxXMLY-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-RxXMLY-acknowledgements.markdown"; sourceTree = ""; }; AE8FF0C7C94471EB15B40751856B8D53 /* ItemEvents.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ItemEvents.swift; path = RxCocoa/iOS/Events/ItemEvents.swift; sourceTree = ""; }; AE9BA3DB7A5A422C613CFBFB2811FEA5 /* Timeout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timeout.swift; path = RxSwift/Observables/Timeout.swift; sourceTree = ""; }; AF66AEF465B9C56A648DA16DBF3D2E4E /* SharedSequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SharedSequence.swift; path = RxCocoa/Traits/SharedSequence/SharedSequence.swift; sourceTree = ""; }; AF9EE6B5C17353C6D306E650D41F6A57 /* RxCocoa-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxCocoa-umbrella.h"; sourceTree = ""; }; AFBBB6581E245F03FD7746F2B3F6838A /* UITableView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITableView+Rx.swift"; path = "RxCocoa/iOS/UITableView+Rx.swift"; sourceTree = ""; }; B00A5B87228E30280657500960598B59 /* LayoutConstraintItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LayoutConstraintItem.swift; path = Source/LayoutConstraintItem.swift; sourceTree = ""; }; B00B2D1E5594681E1384A988B35D66F6 /* WithLatestFrom.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WithLatestFrom.swift; path = RxSwift/Observables/WithLatestFrom.swift; sourceTree = ""; }; B013E2FF6D44578E3E483B417504DD8F /* ReusableKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ReusableKit.xcconfig; sourceTree = ""; }; B09E2CE4824B4A44FE923169E5B3934C /* PrimitiveSequence+Zip+arity.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "PrimitiveSequence+Zip+arity.swift"; path = "RxSwift/Traits/PrimitiveSequence+Zip+arity.swift"; sourceTree = ""; }; B0D0CA0D4B6A0E2EB840F4E5D6C75340 /* _RXObjCRuntime.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _RXObjCRuntime.h; path = RxCocoa/Runtime/include/_RXObjCRuntime.h; sourceTree = ""; }; B0E8C13296CD0445ED73183C8774BA83 /* Kingfisher.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Kingfisher.modulemap; sourceTree = ""; }; B10806D65630D3922CC4632E4B961170 /* BinaryDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BinaryDisposable.swift; path = RxSwift/Disposables/BinaryDisposable.swift; sourceTree = ""; }; B1D09ED075CFA4F5069E951F7B848972 /* UICollectionView+RxReusableKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UICollectionView+RxReusableKit.swift"; path = "Sources/RxReusableKit/UICollectionView+RxReusableKit.swift"; sourceTree = ""; }; B20C215CD03F9EC325B96AD2A4621096 /* ConstraintLayoutSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutSupport.swift; path = Source/ConstraintLayoutSupport.swift; sourceTree = ""; }; B216F1C86A2BB68A807DD4CD8314351F /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B228A61D64CA44B4648977930AEEBF5F /* TYPagerController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TYPagerController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B28CBB9A2FC6BD76A7188D520046176D /* KingfisherOptionsInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherOptionsInfo.swift; path = Sources/KingfisherOptionsInfo.swift; sourceTree = ""; }; B2C6C61F18ADC817A64446B3F214A416 /* Pods_RxXMLY.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxXMLY.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B2E6EA6ADCEAC27A4822132EBAC46122 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = ""; }; B32CA5B611266CA013631C5E362ACECE /* ScheduledItemType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScheduledItemType.swift; path = RxSwift/Schedulers/Internal/ScheduledItemType.swift; sourceTree = ""; }; B3F4C6BA01C9A3737D53D5029569BD25 /* UIPanGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIPanGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UIPanGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; B48B219E8CC99707DBEA39F8FF5389AA /* ImagePrefetcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImagePrefetcher.swift; path = Sources/ImagePrefetcher.swift; sourceTree = ""; }; B599D41042A76B9DF98EB3822B3056BA /* UIPageControl+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIPageControl+Rx.swift"; path = "RxCocoa/iOS/UIPageControl+Rx.swift"; sourceTree = ""; }; B5A668A610822A5EACD5784C5EEE3E17 /* KVORepresentable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KVORepresentable.swift; path = RxCocoa/Foundation/KVORepresentable.swift; sourceTree = ""; }; B68C41252797A96DC3101FEF24BD28A4 /* Moya.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Moya.modulemap; sourceTree = ""; }; B6C6A5E1D3112FA5320C2F8509A9ADD7 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B6DD7C62D3975725987453F154E89E03 /* Kingfisher.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Kingfisher.xcconfig; sourceTree = ""; }; B717B3469C2896926EF05EAF11498DA0 /* _RX.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _RX.h; path = RxCocoa/Runtime/include/_RX.h; sourceTree = ""; }; B7AB99038CF5A8302F1BE2D35B09885A /* MJRefreshBackGifFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackGifFooter.h; path = MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h; sourceTree = ""; }; B7B99FDB86B839402402CF4B9085F867 /* MJRefresh.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MJRefresh.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B7FBCAED115C93A340B4D4817383226A /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B8020BB1D97D4EA3246C9476D7044FE7 /* NSObject+Rx-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "NSObject+Rx-dummy.m"; sourceTree = ""; }; B840B6E9063F05A5F844D55042BC104D /* SharedSequence+Operators+arity.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SharedSequence+Operators+arity.swift"; path = "RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift"; sourceTree = ""; }; B86E459038ACF95C44868462505C56A6 /* TTRangeSliderDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TTRangeSliderDelegate.h; path = Pod/Classes/TTRangeSliderDelegate.h; sourceTree = ""; }; B8D62DD162002F9C56F1C29692DB9FB4 /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = ""; }; B8DAA4F672011BE9BD20D53B1B64E479 /* Disposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Disposable.swift; path = RxSwift/Disposable.swift; sourceTree = ""; }; B97FA317AEF5791CA7299591278DDD85 /* RxPickerViewDataSourceType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxPickerViewDataSourceType.swift; path = RxCocoa/iOS/Protocols/RxPickerViewDataSourceType.swift; sourceTree = ""; }; B9B6B63EED3B0A8A6802499D6992A2CE /* RxDataSources-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxDataSources-umbrella.h"; sourceTree = ""; }; B9CFFF8A56873944159E6B04DBAD45BD /* Deprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Deprecated.swift; path = Sources/RxDataSources/Deprecated.swift; sourceTree = ""; }; BAB7E27C2C9842A03F3BD947DD8493A2 /* IQKeyboardManagerConstantsInternal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IQKeyboardManagerConstantsInternal.swift; path = IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift; sourceTree = ""; }; BACE4EA986227C4D5F468E9FC4DB57BC /* DelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DelegateProxy.swift; path = RxCocoa/Common/DelegateProxy.swift; sourceTree = ""; }; BB9B39409869FB19DBBE9E21A282D0E1 /* PublishRelay+Signal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "PublishRelay+Signal.swift"; path = "RxCocoa/Traits/Signal/PublishRelay+Signal.swift"; sourceTree = ""; }; BC0C35EACA9CABA73CF6D660DB55A64A /* TYPagerController-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "TYPagerController-dummy.m"; sourceTree = ""; }; BC52351249DB2B467541996968B175A0 /* ObjectMapper-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ObjectMapper-dummy.m"; sourceTree = ""; }; BC645F028499DF5A3CB986DD6D4358F1 /* Result.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BC7B9021F90F58D6E367BA8B4C9C2B14 /* ToArray.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToArray.swift; path = RxSwift/Observables/ToArray.swift; sourceTree = ""; }; BCC1C0B2E36AC5340AC67AD9AD286793 /* ObjectMapper.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = ObjectMapper.modulemap; sourceTree = ""; }; BDA8654A5097FB77F91981EC742DE882 /* MultiTarget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultiTarget.swift; path = Sources/Moya/MultiTarget.swift; sourceTree = ""; }; BDC3EE36C7DE6D97F96B2B36B0E557C9 /* ScheduledItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScheduledItem.swift; path = RxSwift/Schedulers/Internal/ScheduledItem.swift; sourceTree = ""; }; BE0C2F5C5C4F8CD822C21482EBE49DD8 /* RxGesture-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RxGesture-dummy.m"; sourceTree = ""; }; BE113770F3941380D55A0F946E7431DE /* TYTabPagerView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYTabPagerView.m; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerView.m; sourceTree = ""; }; BE49DD1F3CB80E0DCB3ED742E3DED62B /* SchedulerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SchedulerType.swift; path = RxSwift/SchedulerType.swift; sourceTree = ""; }; BE4CFCC4E9A965F95EE17202FBB5F310 /* SynchronizedOnType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SynchronizedOnType.swift; path = RxSwift/Concurrency/SynchronizedOnType.swift; sourceTree = ""; }; BE4D4A9C4DD3E20DEF7AD0D414C67442 /* UITabBarItem+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITabBarItem+Rx.swift"; path = "RxCocoa/iOS/UITabBarItem+Rx.swift"; sourceTree = ""; }; BE521621242425215AB95C0D1F947998 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BE96693365ED0583573F43D8F5B57187 /* ImageView+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ImageView+Kingfisher.swift"; path = "Sources/ImageView+Kingfisher.swift"; sourceTree = ""; }; BEB35F8259524796939172BEB9769CB6 /* PriorityQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PriorityQueue.swift; path = Platform/DataStructures/PriorityQueue.swift; sourceTree = ""; }; BEF74C2451A7C311E3C89E75D4746DD4 /* RxScrollViewDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxScrollViewDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift; sourceTree = ""; }; C0112A7EDEEB67FF3C434A3F925DEA88 /* SubscriptionDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SubscriptionDisposable.swift; path = RxSwift/Disposables/SubscriptionDisposable.swift; sourceTree = ""; }; C0155AA3020891E25233E5B541EF2063 /* TYPagerController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYPagerController.h; path = TYPagerControllerDemo/TYPagerController/TYPagerController.h; sourceTree = ""; }; C0764078D276516AEF879D67E95D225E /* Changeset.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Changeset.swift; path = Sources/Differentiator/Changeset.swift; sourceTree = ""; }; C0B487DA905C4F895E0D126C1A062261 /* AnonymousDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnonymousDisposable.swift; path = RxSwift/Disposables/AnonymousDisposable.swift; sourceTree = ""; }; C0B85CEF090EDD7737A7E0F1D0F0F169 /* TTRangeSlider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TTRangeSlider.h; path = Pod/Classes/TTRangeSlider.h; sourceTree = ""; }; C0DB238A8FB8D79A81E67CBB38ECF524 /* Differentiator-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Differentiator-dummy.m"; sourceTree = ""; }; C10FF295CB8B9A0152FA38DF372846A4 /* CompositeDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompositeDisposable.swift; path = RxSwift/Disposables/CompositeDisposable.swift; sourceTree = ""; }; C1DC026D0D8F36437F6F9662A1805D0E /* PublishSubject.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PublishSubject.swift; path = RxSwift/Subjects/PublishSubject.swift; sourceTree = ""; }; C2316CDD5367CCF5307F4C680E70DBBA /* UISearchBar+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UISearchBar+Rx.swift"; path = "RxCocoa/iOS/UISearchBar+Rx.swift"; sourceTree = ""; }; C2617565C24877EE61A697C90E5405A2 /* ReactorKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReactorKit-prefix.pch"; sourceTree = ""; }; C47049CF48166B10EF910F23995AEC84 /* TYCyclePagerTransformLayout.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYCyclePagerTransformLayout.m; path = TYCyclePagerViewDemo/TYCyclePagerView/TYCyclePagerTransformLayout.m; sourceTree = ""; }; C481BF3DA3A78A8E7F5176B802625CC6 /* Deprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Deprecated.swift; path = RxSwift/Deprecated.swift; sourceTree = ""; }; C5A28FC784D68445E20B42FEE607E770 /* Then-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Then-prefix.pch"; sourceTree = ""; }; C5B39EAE40FAC88E85E0EDD5108794F0 /* TYPagerViewLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYPagerViewLayout.h; path = TYPagerControllerDemo/TYPagerController/TYPagerViewLayout.h; sourceTree = ""; }; C5BFA257EAD55FA150B56C6B292EAA44 /* RequestModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestModifier.swift; path = Sources/RequestModifier.swift; sourceTree = ""; }; C60A46D85400354A02A72584F53237D8 /* Optional+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Optional+Extensions.swift"; path = "Sources/Differentiator/Optional+Extensions.swift"; sourceTree = ""; }; C68244972029A1CB9C966D064AB6967F /* MJRefreshGifHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshGifHeader.m; path = MJRefresh/Custom/Header/MJRefreshGifHeader.m; sourceTree = ""; }; C70DA4143C75E4B49245F0F990AF117E /* RxGesture-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxGesture-prefix.pch"; sourceTree = ""; }; C755463902805FE86A327D9AD0B26200 /* RxTabBarDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTabBarDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxTabBarDelegateProxy.swift; sourceTree = ""; }; C7A200DBF260A1DD96FA33642A921C9F /* ConcurrentDispatchQueueScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConcurrentDispatchQueueScheduler.swift; path = RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift; sourceTree = ""; }; C7AF230FEF4F9203FE69CA75CAC56E33 /* PrimitiveSequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PrimitiveSequence.swift; path = RxSwift/Traits/PrimitiveSequence.swift; sourceTree = ""; }; C84F456A91C16DC64488D342C8BC5929 /* Binder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Binder.swift; path = RxCocoa/Common/Binder.swift; sourceTree = ""; }; C8B8D267B8E5E2A789EBA75CCFF121F2 /* SwiftyJSON-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-umbrella.h"; sourceTree = ""; }; CB17D569CE831A4F4E00E2FC816EBBC9 /* ImageProcessor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageProcessor.swift; path = Sources/ImageProcessor.swift; sourceTree = ""; }; CBD70508C00E38F1FE63CED0383CAFE6 /* Platform.Darwin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Platform.Darwin.swift; path = Platform/Platform.Darwin.swift; sourceTree = ""; }; CC6A0E89EEA59CA5659D2EED0C773FD1 /* ImageCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Sources/ImageCache.swift; sourceTree = ""; }; CC6B9B2770629FBEE8FF3829E91ACE5A /* Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Kingfisher.swift; path = Sources/Kingfisher.swift; sourceTree = ""; }; CC94DB3E4DBB1ECF4FA33EBC9EAACFDF /* Switch.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Switch.swift; path = RxSwift/Observables/Switch.swift; sourceTree = ""; }; CD67BEEE3096E3E2BD09DF6158788A6E /* TYTabPagerBarLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYTabPagerBarLayout.h; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerBarLayout.h; sourceTree = ""; }; CDB96EFEBACCC377C2C1852E1187BDE3 /* UIStepper+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIStepper+Rx.swift"; path = "RxCocoa/iOS/UIStepper+Rx.swift"; sourceTree = ""; }; CE2E4D442D52B0894ED51620B7A11E8B /* RxTextStorageDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTextStorageDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxTextStorageDelegateProxy.swift; sourceTree = ""; }; CEA052CCABE96871EBBAFD7D6C959A06 /* AccessTokenPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AccessTokenPlugin.swift; path = Sources/Moya/Plugins/AccessTokenPlugin.swift; sourceTree = ""; }; CEC01AC2D958140E2A090D7B632F5567 /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ControlProperty+Driver.swift"; path = "RxCocoa/Traits/Driver/ControlProperty+Driver.swift"; sourceTree = ""; }; CF47A0081987D5825B05757F83159AE9 /* UIButton+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIButton+Kingfisher.swift"; path = "Sources/UIButton+Kingfisher.swift"; sourceTree = ""; }; D06DCA6A0B6616240EC75F089FCB605D /* AnyObserver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnyObserver.swift; path = RxSwift/AnyObserver.swift; sourceTree = ""; }; D0C3B1C0259FBCD7A10525F73D370CF5 /* UIActivityIndicatorView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIActivityIndicatorView+Rx.swift"; path = "RxCocoa/iOS/UIActivityIndicatorView+Rx.swift"; sourceTree = ""; }; D1CF8E77098D7789AB0555F960E5B430 /* ConstraintPriority.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintPriority.swift; path = Source/ConstraintPriority.swift; sourceTree = ""; }; D2A7C9B0E3259DE8E0442244250D596F /* ConstraintConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintConfig.swift; path = Source/ConstraintConfig.swift; sourceTree = ""; }; D2D55E77F429A861A4AEA5872801C81A /* SnapKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SnapKit-umbrella.h"; sourceTree = ""; }; D2E1B85E14851EF923C2BD626BE22587 /* Create.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Create.swift; path = RxSwift/Observables/Create.swift; sourceTree = ""; }; D302B83E9CE0D2B43DFA34A03D0DBE2B /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D3869DF0902D254BD40D6B02E11A8B52 /* IdentifiableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IdentifiableType.swift; path = Sources/Differentiator/IdentifiableType.swift; sourceTree = ""; }; D3F7DB8C08BAE7F26A0EE85DA33C3905 /* NSObject+Rx+RawRepresentable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSObject+Rx+RawRepresentable.swift"; path = "RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift"; sourceTree = ""; }; D4A33E97EE17B95FCE36DEBE7ACE03F3 /* Result-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Result-dummy.m"; sourceTree = ""; }; D52211FFC97D4BB7CF97DA504F823D77 /* MJRefresh-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MJRefresh-dummy.m"; sourceTree = ""; }; D57C9A2A08ACCD09288F0F61A6D87B43 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D58AEC2CC3F21D23D64B1106A4C82CA2 /* IQKeyboardManager.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = IQKeyboardManager.bundle; path = IQKeyboardManagerSwift/Resources/IQKeyboardManager.bundle; sourceTree = ""; }; D5B52B71645C13D27A3C4B3A58EA2440 /* RxGesture.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxGesture.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D609A4B215C782073049E462F652D85D /* TYTabPagerBarCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = TYTabPagerBarCell.h; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerBarCell.h; sourceTree = ""; }; D69D61C111C8F4287DFE589110A04EA4 /* UIAlertAction+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIAlertAction+Rx.swift"; path = "RxCocoa/iOS/UIAlertAction+Rx.swift"; sourceTree = ""; }; D6AD0538BC7F97DC3694296C53DBE9C8 /* Then-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Then-dummy.m"; sourceTree = ""; }; D71887F7534D1360E1CF05E79FDAC471 /* Stub.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Stub.swift; path = Sources/ReactorKit/Stub.swift; sourceTree = ""; }; D76F782371F7D6EF97DE209867481EA5 /* HistoricalSchedulerTimeConverter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HistoricalSchedulerTimeConverter.swift; path = RxSwift/Schedulers/HistoricalSchedulerTimeConverter.swift; sourceTree = ""; }; D783020743D1A18018EE614D78D965A2 /* URLNavigator-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "URLNavigator-umbrella.h"; sourceTree = ""; }; D819115F4043DA9F9126B97A2AAD84BD /* UIPickerView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIPickerView+Rx.swift"; path = "RxCocoa/iOS/UIPickerView+Rx.swift"; sourceTree = ""; }; D85A799653D3BC7BA7BDB6BD691336F8 /* URLRequest+Encoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLRequest+Encoding.swift"; path = "Sources/Moya/URLRequest+Encoding.swift"; sourceTree = ""; }; D8FDDBEBD3900E6B6B3DE7D9D96C0CFA /* URLSession+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLSession+Rx.swift"; path = "RxCocoa/Foundation/URLSession+Rx.swift"; sourceTree = ""; }; D94169655CF6B1F44A9F5A11128DFBB5 /* UINavigationController+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UINavigationController+Rx.swift"; path = "RxCocoa/iOS/UINavigationController+Rx.swift"; sourceTree = ""; }; DA67E4E3F08A96BF5B2596ED0FC34196 /* DefaultIfEmpty.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultIfEmpty.swift; path = RxSwift/Observables/DefaultIfEmpty.swift; sourceTree = ""; }; DA8C0C17C65F493BBDD9F7C413C2C378 /* IdentifiableValue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IdentifiableValue.swift; path = Sources/Differentiator/IdentifiableValue.swift; sourceTree = ""; }; DA9C2D5FD4A6E72AF207648A0646DCCF /* RxCollectionViewDataSourceType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewDataSourceType.swift; path = RxCocoa/iOS/Protocols/RxCollectionViewDataSourceType.swift; sourceTree = ""; }; DB2A4AE8B923CFBB0D0531CB91C712CE /* MJRefresh.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MJRefresh.xcconfig; sourceTree = ""; }; DB7464D95C191D51D7944010316440FF /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Sources/Moya/Response.swift; sourceTree = ""; }; DB98DBDF6250E6EDD9D4711CCA72CDE3 /* URLMatcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLMatcher.swift; path = Sources/URLMatcher/URLMatcher.swift; sourceTree = ""; }; DBF02AD8B77821AA7137D153FD7D26DC /* UIImageView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIImageView+Rx.swift"; path = "RxCocoa/iOS/UIImageView+Rx.swift"; sourceTree = ""; }; DC01CD4F0AF25E87AE052DB77D446F12 /* MJRefreshBackNormalFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackNormalFooter.m; path = MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m; sourceTree = ""; }; DC30C48A2548E77051CB43345238F5E4 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = ""; }; DC3268ED5E6ABB0ABCDEEE48CE0CD78F /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = ""; }; DD1C4965E4534F094C7E5D0417A804AC /* DistinctUntilChanged.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DistinctUntilChanged.swift; path = RxSwift/Observables/DistinctUntilChanged.swift; sourceTree = ""; }; DD71B5E50804962A47932EB53600027A /* Moya+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Moya+Alamofire.swift"; path = "Sources/Moya/Moya+Alamofire.swift"; sourceTree = ""; }; DD77C22F0E17F82C01E907DC393087DA /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DD7CFB953733E5734A907460B2C40BCE /* ReusableKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReusableKit-umbrella.h"; sourceTree = ""; }; DE17B34AF63AF3BB97075A6D295FDCB3 /* RxAlamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxAlamofire-prefix.pch"; sourceTree = ""; }; DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DE2A6BA18853C4598D21FDBBD902BD32 /* IQKeyboardManagerSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = IQKeyboardManagerSwift.modulemap; sourceTree = ""; }; DE31A7BD0FFA90C66DA1DEBAD573E053 /* UILabel+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UILabel+Rx.swift"; path = "RxCocoa/iOS/UILabel+Rx.swift"; sourceTree = ""; }; DE50048633BEA4A961DBEE8DC7631174 /* SwiftyColor.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = SwiftyColor.modulemap; sourceTree = ""; }; DEB5737B2DA2DF3423F29C0CBB7542DA /* Range.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Range.swift; path = RxSwift/Observables/Range.swift; sourceTree = ""; }; E00D0BE5D24B8BDE0B2711827A346D65 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E010BA57C8A6D94C6A65D63FA871BFCB /* RxGesture-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxGesture-umbrella.h"; sourceTree = ""; }; E018F18F377B9190734B1D9EB8160AD7 /* Skip.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Skip.swift; path = RxSwift/Observables/Skip.swift; sourceTree = ""; }; E02E53CE029ADA867E576C62B9054390 /* RxAlamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxAlamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E031F5913ADD2E4B2FE376BDDB99068C /* RxDataSources-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RxDataSources-dummy.m"; sourceTree = ""; }; E03BFCA5D027189D74E5502CC0D58735 /* Logging.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Logging.swift; path = RxCocoa/Foundation/Logging.swift; sourceTree = ""; }; E127D4A1CC515D5A1562D756ADE9089E /* ShareReplayScope.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ShareReplayScope.swift; path = RxSwift/Observables/ShareReplayScope.swift; sourceTree = ""; }; E168570235239A51FCD669B5E01BD944 /* TTRangeSlider.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TTRangeSlider.xcconfig; sourceTree = ""; }; E1D44E21411F23F3505F31B444A1A104 /* TYCyclePagerView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYCyclePagerView.m; path = TYCyclePagerViewDemo/TYCyclePagerView/TYCyclePagerView.m; sourceTree = ""; }; E3508AF1777C25C4BC5CE6002832B9BF /* RxCollectionViewDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift; sourceTree = ""; }; E4051F928839DBFCB64F5087D54C40E3 /* CombineLatest+Collection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "CombineLatest+Collection.swift"; path = "RxSwift/Observables/CombineLatest+Collection.swift"; sourceTree = ""; }; E415D1E19F77D7279C5F140411D69E67 /* First.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = First.swift; path = RxSwift/Observables/First.swift; sourceTree = ""; }; E417881CF3E530ABBE356AD806F81649 /* Materialize.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Materialize.swift; path = RxSwift/Observables/Materialize.swift; sourceTree = ""; }; E420D6F4781A96E122D216CBDA976615 /* MainScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MainScheduler.swift; path = RxSwift/Schedulers/MainScheduler.swift; sourceTree = ""; }; E4373BBA3EC79125E217AF8056418360 /* MJRefreshBackFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackFooter.h; path = MJRefresh/Base/MJRefreshBackFooter.h; sourceTree = ""; }; E441DE19DC813E660F49CCA5AF0FA3F5 /* EnumOperators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EnumOperators.swift; path = Sources/EnumOperators.swift; sourceTree = ""; }; E4CFC30F3E8C50C2772DDC74F2AC0626 /* TYCyclePagerView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "TYCyclePagerView-dummy.m"; sourceTree = ""; }; E5D465BD6EB72259B11615DB9761D3DB /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Sources/Moya/MultipartFormData.swift; sourceTree = ""; }; E6BE51ED2DAF09F29A0DDADC0911D49B /* RxTableViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxTableViewReactiveArrayDataSource.swift; path = RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift; sourceTree = ""; }; E6D2C080BD70A34F38B01F04F8C066D6 /* SharedTypes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SharedTypes.swift; path = Pod/Classes/SharedTypes.swift; sourceTree = ""; }; E6F687744CD01C74158C6A8CA1C9B497 /* MJRefreshAutoStateFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoStateFooter.m; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m; sourceTree = ""; }; E7023AE71AB1962FA73C0D900E6564BE /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = ""; }; E74A11FE1DBDB7EA6BDB703F6B58F460 /* Do.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Do.swift; path = RxSwift/Observables/Do.swift; sourceTree = ""; }; E75CBFE107A4EA81EDD844CA7F371A0B /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E800F1732A0D7B4A6640BE266F14DDC8 /* _RXKVOObserver.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _RXKVOObserver.h; path = RxCocoa/Runtime/include/_RXKVOObserver.h; sourceTree = ""; }; E8D611801824EEACA86E5FF945882746 /* RxAlamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = RxAlamofire.modulemap; sourceTree = ""; }; E8D6DDBD1576F25B98F7059832A190A3 /* Pods-RxXMLY-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-RxXMLY-resources.sh"; sourceTree = ""; }; E915131DA668033B6756FDE94DD6C130 /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E91D5BAB4A332A7524C3625B57B61925 /* ObservableConvertibleType+Signal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ObservableConvertibleType+Signal.swift"; path = "RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift"; sourceTree = ""; }; E9246ADE8730ABD21F1BBC59333EA31E /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E99B4913C9AF498445CF9BB785FFBFFF /* TYPageControl.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYPageControl.m; path = TYCyclePagerViewDemo/TYCyclePagerView/TYPageControl.m; sourceTree = ""; }; E9A37A86FC5231B8DFC0C17337CC40F5 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; EA3EFB292391C42C14DBF00E49C533EC /* NSBundle+MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSBundle+MJRefresh.h"; path = "MJRefresh/NSBundle+MJRefresh.h"; sourceTree = ""; }; EA47B9E0DFFD8D15F19F5033E63B9026 /* CurrentThreadScheduler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CurrentThreadScheduler.swift; path = RxSwift/Schedulers/CurrentThreadScheduler.swift; sourceTree = ""; }; EAAE74B8E84AC1A31796559B46480858 /* RecursiveLock.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RecursiveLock.swift; path = Platform/RecursiveLock.swift; sourceTree = ""; }; EC29C744A1F98EEA2D40646969A57510 /* DataTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataTransform.swift; path = Sources/DataTransform.swift; sourceTree = ""; }; EC5873F3553D2C072C19D661AA844D1C /* MJRefreshAutoGifFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoGifFooter.m; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m; sourceTree = ""; }; EC5D50260AEE66CC8FD97C3CF9E10E7F /* Then.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Then.xcconfig; sourceTree = ""; }; EC766D153E5F9EEED025A6A61E553812 /* MJRefresh.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = MJRefresh.modulemap; sourceTree = ""; }; EC8A134B3C9407540FCDEC584183133D /* Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Rx.swift; path = RxSwift/Rx.swift; sourceTree = ""; }; ECECB25BC65301EB453951C200FFCD22 /* RxSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxSwift-prefix.pch"; sourceTree = ""; }; EDA69717822ACCAAF367E9F539039219 /* AsMaybe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AsMaybe.swift; path = RxSwift/Observables/AsMaybe.swift; sourceTree = ""; }; EE68236572EEA4E94A38244743901FE9 /* UIScrollView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIScrollView+Rx.swift"; path = "RxCocoa/iOS/UIScrollView+Rx.swift"; sourceTree = ""; }; EF04BEEB09DE1352D9F767144EF0FD50 /* ScheduledDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScheduledDisposable.swift; path = RxSwift/Disposables/ScheduledDisposable.swift; sourceTree = ""; }; EFD5B31121E42968628607F4CA11A4BC /* Differentiator.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Differentiator.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F01E5E91EC940C05D243E33611C9CA5C /* SnapKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SnapKit-prefix.pch"; sourceTree = ""; }; F03D2DC1D0CBA5B7FB557A21D0DA5771 /* TransformOperators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformOperators.swift; path = Sources/TransformOperators.swift; sourceTree = ""; }; F0758B65380EFC2497BC08F9A7E4F402 /* DisposeBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DisposeBase.swift; path = RxSwift/Disposables/DisposeBase.swift; sourceTree = ""; }; F08868A7AD5BECFD90CC22A62671DC82 /* SerialDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SerialDisposable.swift; path = RxSwift/Disposables/SerialDisposable.swift; sourceTree = ""; }; F1AC2191C0BA5AC761428DAD97E25B05 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F1D0EF5DC8B377434B4B9FAA777B65E0 /* MJRefreshBackNormalFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackNormalFooter.h; path = MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h; sourceTree = ""; }; F28EC587F2484F4548370BCE6AA22C0C /* UIDatePicker+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIDatePicker+Rx.swift"; path = "RxCocoa/iOS/UIDatePicker+Rx.swift"; sourceTree = ""; }; F293CCDAB139CAA1824ACDC4BB8DE2EA /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F364B3A757735A453DB16E22F1A2D52A /* Lock.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Lock.swift; path = RxSwift/Concurrency/Lock.swift; sourceTree = ""; }; F384E81160731D07A8472A16F0EF1EFD /* Bag+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Bag+Rx.swift"; path = "RxSwift/Extensions/Bag+Rx.swift"; sourceTree = ""; }; F3AFC24D86F7304F0B4AF511C9B9970A /* NSDecimalNumberTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NSDecimalNumberTransform.swift; path = Sources/NSDecimalNumberTransform.swift; sourceTree = ""; }; F4851621D756478D43208A461240D7CF /* ISO8601DateTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ISO8601DateTransform.swift; path = Sources/ISO8601DateTransform.swift; sourceTree = ""; }; F4E3F42EFB30F56006250129E4BF5ABF /* TYPagerController-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TYPagerController-umbrella.h"; sourceTree = ""; }; F4F172EFA622B78B8E948E1F153B1BDD /* Task.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Task.swift; path = Sources/Moya/Task.swift; sourceTree = ""; }; F5367CAC7283271F0E12047EE1E54190 /* DispatchQueue+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Extensions.swift"; path = "Platform/DispatchQueue+Extensions.swift"; sourceTree = ""; }; F570C010C51F2EDFA2B29E61A28988D2 /* Signal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Signal.swift; path = RxCocoa/Traits/Signal/Signal.swift; sourceTree = ""; }; F5EB9FACBAF778C2D7E39429AAFFADE7 /* ConstraintAttributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintAttributes.swift; path = Source/ConstraintAttributes.swift; sourceTree = ""; }; F6367B989A05CB68EFF4B1822D8DB33E /* UITextView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITextView+Rx.swift"; path = "RxCocoa/iOS/UITextView+Rx.swift"; sourceTree = ""; }; F68D0C4E903F4B9E77676032554A5278 /* Placeholder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Placeholder.swift; path = Sources/Placeholder.swift; sourceTree = ""; }; F71B53FF12451C5503271983A3FE932B /* Then-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Then-umbrella.h"; sourceTree = ""; }; F728D985BB2068A70834B0E454690223 /* NSTextStorage+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSTextStorage+Rx.swift"; path = "RxCocoa/iOS/NSTextStorage+Rx.swift"; sourceTree = ""; }; F73232742568F2DC4E279896B22BA8A3 /* TransformType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformType.swift; path = Sources/TransformType.swift; sourceTree = ""; }; F76E37DE05B72E5ACE3CC2FEA524DC00 /* UIScrollView+MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+MJRefresh.h"; path = "MJRefresh/UIScrollView+MJRefresh.h"; sourceTree = ""; }; F79A80A6FF76E139A757BFA4110503E7 /* KVORepresentable+Swift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "KVORepresentable+Swift.swift"; path = "RxCocoa/Foundation/KVORepresentable+Swift.swift"; sourceTree = ""; }; F7B06E614268F88A37528BE5B8A08933 /* NotificationCenter+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NotificationCenter+Rx.swift"; path = "RxCocoa/Foundation/NotificationCenter+Rx.swift"; sourceTree = ""; }; F9218E907F18A770B1DFDE2EDEAA3480 /* MJRefreshBackStateFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackStateFooter.h; path = MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h; sourceTree = ""; }; F9B85640D4BCBBFE6662A71B6EE98AE9 /* ReplaySubject.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ReplaySubject.swift; path = RxSwift/Subjects/ReplaySubject.swift; sourceTree = ""; }; FA64F8A7EA7F1D019BA4637FA772850D /* TYTabPagerBarLayout.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = TYTabPagerBarLayout.m; path = TYPagerControllerDemo/TYPagerController/TabPager/TYTabPagerBarLayout.m; sourceTree = ""; }; FB352C9155E142C44FAE86885DBC3E62 /* IQUIScrollView+Additions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "IQUIScrollView+Additions.swift"; path = "IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift"; sourceTree = ""; }; FB6BED57753CCBC597942DFD41588338 /* Then.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Then.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FB757B34331F94A8CE2ABFE0AC0050D4 /* ImageDownloader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageDownloader.swift; path = Sources/ImageDownloader.swift; sourceTree = ""; }; FB8C71D5C39C423BB61425ABDC60B40B /* UIScrollView+MJExtension.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+MJExtension.h"; path = "MJRefresh/UIScrollView+MJExtension.h"; sourceTree = ""; }; FC0E5E64017E2B544B9C4365BF0DBBF5 /* KVORepresentable+CoreGraphics.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "KVORepresentable+CoreGraphics.swift"; path = "RxCocoa/Foundation/KVORepresentable+CoreGraphics.swift"; sourceTree = ""; }; FC57AF3FEB7D99891C6F7BD5610269D7 /* MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefresh.h; path = MJRefresh/MJRefresh.h; sourceTree = ""; }; FCA359E28EBA408A36C0296FA25A6731 /* ConstraintInsets.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintInsets.swift; path = Source/ConstraintInsets.swift; sourceTree = ""; }; FD5F22C193A3AD6545BDE4B91729668D /* UIScreenEdgePanGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIScreenEdgePanGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UIScreenEdgePanGestureRecognizer+RxGesture.swift"; sourceTree = ""; }; FDD6B542F72BB78245FF7A817E211A9D /* TYCyclePagerView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TYCyclePagerView-prefix.pch"; sourceTree = ""; }; FE2F015C7CCD3096659FF2450B75E15F /* Result-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Result-prefix.pch"; sourceTree = ""; }; FE622F585E2DE18232A22FD2E069BE75 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; FF01C2ED8BF6578C8212FD3EAC90D3E2 /* Completable+AndThen.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Completable+AndThen.swift"; path = "RxSwift/Traits/Completable+AndThen.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 0342A957F1A5702D324BDF4037D7D6B3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( EE7B1C447D0CF446C179EA74E5646A94 /* Foundation.framework in Frameworks */, B14375A0B7445CA462819C7468B3EF21 /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 1AAD91176E5F5F9AD062AD678F6746E4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( A2D9F9EFFDFE9C19DA842E826B06BCEB /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 1E430C90A89892CE9179EEDDCBFED27E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( AC1E75DFB1009851DBAA58FF21A18D95 /* Foundation.framework in Frameworks */, 8D70BCBAC4C9EBC91472AB223B7485AE /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 23AD706635149AE5F75579590BA4B09C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( EADFC7E11D0CCEE93B2F2B90B02C8CF7 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 23D3B010AD4A853FE7ED1D97A480065F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 5539A3CD2B653BA23D68AED9ADEA40C5 /* Alamofire.framework in Frameworks */, DB912D996DC177901DACA697F2EF96F9 /* Foundation.framework in Frameworks */, 5B72B69DD603A056FE26772F3CFD56E8 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 2CCEFAA691F3229A3313687F2CC6981E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E3EF505A67060CFB3E503F5B79D6E909 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 2E345FD99987A0C2F53C3EB5089F2023 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 55F024586B17FD47A9D6CACB88B6BED3 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 2E911FDC90F1E7ACD11F0B68CC7B1FCA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E272024F3BB38967141313D5CB8E4120 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 31195B5F7226702168F8D241576F56D5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 5C11C8A05966C040DCDE2E2B1B0C8D7A /* Foundation.framework in Frameworks */, BADF32BF243CAB2D89D072A0C2A9D791 /* RxCocoa.framework in Frameworks */, 03649B1E46C35FC5D9BC8CE4EFC3AF5D /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 4B7C72EFD055B004BD8D36238863B2D5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DBCCE10306122D22ABD5132028DEC06B /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 5642B75CBC2CCDA7E44A7707DD089AF1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 34E6139A80CA53099913276CC5157304 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 674E18361DBF81F73164E63E79DDC140 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 29AEA44F673962935BCE359544C2EE0D /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 69C6F60DD2457704809BD1D3D4543AD9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D9EAAE4A13D0E04E0557CA100CE0A2BF /* Foundation.framework in Frameworks */, 7335E13169D3E91E60ABF132EE768E99 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 73BF36D607B79591CC85E897840E2802 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E77D52EE7D897833F4A8441A8F2477B5 /* Alamofire.framework in Frameworks */, 2A7AE49F85F20FEDA1833CAB32D7933D /* Foundation.framework in Frameworks */, 7F47526A3CC008FF7F1D3232D3435559 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 748266AA260D6CE008E3FF2C76A52D04 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 089250578AFBD4AD861843847252D462 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 7981EDCD0900235B2B3D12DBC172DB34 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 4546CA3500EDD39DBE8AD016CD79BDFC /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 81A5C814ACEB86CB06CD6C51383982FE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BB04209DDB72CD325AE09D2EC3EBC380 /* CoreGraphics.framework in Frameworks */, A25F717B5E0C4E6A6DE933B0BF0F6417 /* Foundation.framework in Frameworks */, 81A2F975614A9B94F265CD3BA816B0A4 /* QuartzCore.framework in Frameworks */, EE20C85515D53DE61D8435080677F58E /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 943D1D0C588FD46C297C76EB927A6963 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 47C3E46E339F974D2B413D2F96BD02B7 /* Differentiator.framework in Frameworks */, F740D92AE1DF1156C9BE899D7A4FE12A /* Foundation.framework in Frameworks */, 89DB813358F7013E9E1AAA822417FE86 /* RxCocoa.framework in Frameworks */, C2F4A31052D8F8C636829A7109A45DB2 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 99195E4207764744AEC07ECCBCD550EB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 73B9C996AED49ED7CF8EC2A6F1738059 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; AABC11B0DB149687903FAC188E5420A1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 2BF0E4469C13CAA14361A3D5B07E1706 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; C0B9722E352CF90C920286E6F029D3C0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 52416A6AC7387ECACCC45596498D96AD /* CFNetwork.framework in Frameworks */, 0CA6357476373C221D44C498AA1D65FE /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; C4F4068871A73EB6FBE8042AF2063595 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 78C198E9BB56FB59BD494C8FA759645B /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; C9E038B57740EF2F804D34B1C9393F02 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 019D5A551A09E129AE3D95BCB378028E /* Foundation.framework in Frameworks */, AB9F26D130FF443CBE0DA2ADE2E27479 /* RxCocoa.framework in Frameworks */, C084E86C68138F0227379212201B1D11 /* RxSwift.framework in Frameworks */, 51D96676F916DC1EB1EEA5F91481E01C /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; E14E1AD13C72905A9744DCFC4CB6754B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( C446D343DA5F7767B64807C921A7BA4A /* Foundation.framework in Frameworks */, E37B94CD6DB302F6D233BD878772B868 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; E6817D3D95D3783EE1F540DE3EA42D19 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 0C83C395557B207600B4BFEFB71F662D /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0065A8C3C82D11C680574000E0D0A173 /* Support Files */ = { isa = PBXGroup; children = ( 2382EF1B0260B04756F14D3A5B99D192 /* Info.plist */, 4C467041F0E9683A1B964FB3053EFBD7 /* TYCyclePagerView.modulemap */, 9CEA02DF9281493AB02811A381B532ED /* TYCyclePagerView.xcconfig */, E4CFC30F3E8C50C2772DDC74F2AC0626 /* TYCyclePagerView-dummy.m */, FDD6B542F72BB78245FF7A817E211A9D /* TYCyclePagerView-prefix.pch */, A5E925D1347A4A7AE44D07D3342D1BCB /* TYCyclePagerView-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/TYCyclePagerView"; sourceTree = ""; }; 00B445FBF5C55D58619DD22ADD326E0B /* RxAlamofire */ = { isa = PBXGroup; children = ( C2E4BC97956884C852E7E3676114F4CD /* Core */, EEBFA1F60BB4035823034A2E49888E19 /* Support Files */, ); path = RxAlamofire; sourceTree = ""; }; 013689684E64F7EE37E9EED321657B7F /* SnapKit */ = { isa = PBXGroup; children = ( 52A0824858ABB06E40B1588D14FF9498 /* Constraint.swift */, F5EB9FACBAF778C2D7E39429AAFFADE7 /* ConstraintAttributes.swift */, D2A7C9B0E3259DE8E0442244250D596F /* ConstraintConfig.swift */, 5D28FC1E29A3FEC5D1CF82FC68A09E26 /* ConstraintConstantTarget.swift */, 5CB1A48DF38EB6B4A32ADD9DF79B73FF /* ConstraintDescription.swift */, 4A6B64332C20EFFA0F9444871E3166E2 /* ConstraintDSL.swift */, FCA359E28EBA408A36C0296FA25A6731 /* ConstraintInsets.swift */, 610AEB49CFD63698A27742F484C3B01B /* ConstraintInsetTarget.swift */, 10DC35A2E29CA2B46E24A2F022E9163B /* ConstraintItem.swift */, 4086B02C434EDF398B3DEAC9194E36E9 /* ConstraintLayoutGuide.swift */, 46E1355C3CBDC013D697081398F8B462 /* ConstraintLayoutGuide+Extensions.swift */, 434BF576D15902B83DA5D893260FA1D6 /* ConstraintLayoutGuideDSL.swift */, B20C215CD03F9EC325B96AD2A4621096 /* ConstraintLayoutSupport.swift */, 705EED60B52573B696D1FD8B6CB52FC2 /* ConstraintLayoutSupportDSL.swift */, 424B56D99302A846FFD60288ED3225A3 /* ConstraintMaker.swift */, 222AACF264926205DACCB10D9C8464D7 /* ConstraintMakerEditable.swift */, 0768925949DA79DC0DB78B89D10ECB45 /* ConstraintMakerExtendable.swift */, 83F0BF62BA5033111F22B49D2DA78D30 /* ConstraintMakerFinalizable.swift */, 8F92102426225DFFC6D512690BD4167B /* ConstraintMakerPriortizable.swift */, 1FA2A35E8B50492B40B48AC0A94A8904 /* ConstraintMakerRelatable.swift */, 1B6E2178443AA8FE2901D19303B3B8F9 /* ConstraintMultiplierTarget.swift */, 7E2E1626B3D838551A7744F976FEB850 /* ConstraintOffsetTarget.swift */, D1CF8E77098D7789AB0555F960E5B430 /* ConstraintPriority.swift */, 7B37D88DF834AFA3231D9F6EF76598D8 /* ConstraintPriorityTarget.swift */, 328686532CB38406B7BB3A32945B6474 /* ConstraintRelatableTarget.swift */, 1C458E0D7CD6F0DD83AC852BC8419AD7 /* ConstraintRelation.swift */, 81B1BE2330946471DDB4A3A11D43E0B8 /* ConstraintView.swift */, 02ACAB88021BEBB2FCA8B6883F2CE332 /* ConstraintView+Extensions.swift */, A468830C4A1700A0C17545C662543173 /* ConstraintViewDSL.swift */, 1D315F44377FFD3AEDE15898C2971BEC /* Debugging.swift */, 553E666C8126DEAEA023EE677226741F /* LayoutConstraint.swift */, B00A5B87228E30280657500960598B59 /* LayoutConstraintItem.swift */, 1EE2A49804E43DA5A796136D1D79109F /* Typealiases.swift */, 01A8496A2160E940A6246AB063ADFA02 /* UILayoutSupport+Extensions.swift */, C87EF0D2F5E72ED8D8AE2A78F9590FBF /* Support Files */, ); path = SnapKit; sourceTree = ""; }; 02CB0B03CB75DEE740DEEDAFC9A4B34D /* Frameworks */ = { isa = PBXGroup; children = ( A1A112A635DA48EF8CB77AF4F3AE614D /* Alamofire.framework */, A7F044D3460E87057EB82CCDD5C4D545 /* Differentiator.framework */, 266FFDCA364C275317DA17C75FA9B1B3 /* Result.framework */, 77AB7A6BBF44C678FD2D852835009017 /* RxCocoa.framework */, DE28FDBE90B525AD5F5FF81C344D3D2A /* RxSwift.framework */, D5050FC1A785C83B3ADD912414E51051 /* iOS */, ); name = Frameworks; sourceTree = ""; }; 0E9D9F01EDD13FD4E5C0AB6FD5BB2A5A /* Support Files */ = { isa = PBXGroup; children = ( B6C6A5E1D3112FA5320C2F8509A9ADD7 /* Info.plist */, 74418F5529BCA8630BE486D0F6A76D6C /* TTRangeSlider.modulemap */, E168570235239A51FCD669B5E01BD944 /* TTRangeSlider.xcconfig */, A27C7B9B2AA2EBC3F3078495A3B4B248 /* TTRangeSlider-dummy.m */, 9DB0EFCE41B9CC2A562FFA3E9B2C8CD1 /* TTRangeSlider-prefix.pch */, 18601CAC220AFC4C4A67E4C19A37FA0C /* TTRangeSlider-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/TTRangeSlider"; sourceTree = ""; }; 1064728F3888D15A87C94E495EFE16CC /* Resources */ = { isa = PBXGroup; children = ( 0A7149E89BECF86F1AE3C3567AB16589 /* MJRefresh.bundle */, ); name = Resources; sourceTree = ""; }; 125AE60B5B1A09953287C270122642EE /* Resources */ = { isa = PBXGroup; children = ( D58AEC2CC3F21D23D64B1106A4C82CA2 /* IQKeyboardManager.bundle */, ); name = Resources; sourceTree = ""; }; 19AF7A80FF7FACF9DA96ECF7C6C84AE6 /* Support Files */ = { isa = PBXGroup; children = ( 3565F6A3B549B0D54D7B9937CBDF22F5 /* Info.plist */, 6C6A3B044CC344E98ACD7182BCE0685D /* TYPagerController.modulemap */, 50A9B85F6C0935020B51702CEF26AC84 /* TYPagerController.xcconfig */, BC0C35EACA9CABA73CF6D660DB55A64A /* TYPagerController-dummy.m */, 9158E32ACD5A2977E6DE91E13F5C7A66 /* TYPagerController-prefix.pch */, F4E3F42EFB30F56006250129E4BF5ABF /* TYPagerController-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/TYPagerController"; sourceTree = ""; }; 1B1EA4443A2498E941CD029F27E9639D /* RxSwift */ = { isa = PBXGroup; children = ( B1D09ED075CFA4F5069E951F7B848972 /* UICollectionView+RxReusableKit.swift */, 7B3743F6F50232B4A27328B734F963F8 /* UITableView+RxReusableKit.swift */, ); name = RxSwift; sourceTree = ""; }; 22A086CB1B582E984EBA7863AED9A385 /* Support Files */ = { isa = PBXGroup; children = ( 5459AEC7839E1C793C220140241E6CD8 /* Info.plist */, 4629139792CEE6CCF32F59002002790D /* RxCocoa.modulemap */, 351B9B4056B3818966E04C76590FC3B7 /* RxCocoa.xcconfig */, 58D1D78FFF97A04C18826C9383B3DFBD /* RxCocoa-dummy.m */, 0E79B5AC02467F0FC951B7F0808BE955 /* RxCocoa-prefix.pch */, AF9EE6B5C17353C6D306E650D41F6A57 /* RxCocoa-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/RxCocoa"; sourceTree = ""; }; 24DBE0B1E85B2506F4E5B030BBFE143F /* Support Files */ = { isa = PBXGroup; children = ( D57C9A2A08ACCD09288F0F61A6D87B43 /* Info.plist */, A212CBEA9196364FF0A6F2FA43D9CE76 /* ReactorKit.modulemap */, 319F0938C83A11D7010E8A6B1A8BC27E /* ReactorKit.xcconfig */, 6EADA7DDE939B839BF4574A8A38504C9 /* ReactorKit-dummy.m */, C2617565C24877EE61A697C90E5405A2 /* ReactorKit-prefix.pch */, 1BC61193B3F56535040C0A61FA71C455 /* ReactorKit-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/ReactorKit"; sourceTree = ""; }; 2A11EEEC2D225026B3E7D7223F9F7A6F /* MJRefresh */ = { isa = PBXGroup; children = ( FC57AF3FEB7D99891C6F7BD5610269D7 /* MJRefresh.h */, 38B56F8F2AC8700738ABD303DD4F3606 /* MJRefreshAutoFooter.h */, 23859CF12D4131E1E0B8B0209A00B878 /* MJRefreshAutoFooter.m */, 1A0EE75F34E2C1B1E6254873E3066CEA /* MJRefreshAutoGifFooter.h */, EC5873F3553D2C072C19D661AA844D1C /* MJRefreshAutoGifFooter.m */, 40B36A7D5D671C2D2DCB124A4EE87A87 /* MJRefreshAutoNormalFooter.h */, 0D076E9D89814329C802E72B0C0229A2 /* MJRefreshAutoNormalFooter.m */, 44409A050584D330FC1B6F6D3CD9B53E /* MJRefreshAutoStateFooter.h */, E6F687744CD01C74158C6A8CA1C9B497 /* MJRefreshAutoStateFooter.m */, E4373BBA3EC79125E217AF8056418360 /* MJRefreshBackFooter.h */, 6D4BA91A34AD8C308124A2BAC574B3B9 /* MJRefreshBackFooter.m */, B7AB99038CF5A8302F1BE2D35B09885A /* MJRefreshBackGifFooter.h */, 43F0F183E8964F9C8D16E30F45CF6844 /* MJRefreshBackGifFooter.m */, F1D0EF5DC8B377434B4B9FAA777B65E0 /* MJRefreshBackNormalFooter.h */, DC01CD4F0AF25E87AE052DB77D446F12 /* MJRefreshBackNormalFooter.m */, F9218E907F18A770B1DFDE2EDEAA3480 /* MJRefreshBackStateFooter.h */, 0BF9B78A8693992A08F4F159CCDE50CA /* MJRefreshBackStateFooter.m */, 91EFB17C939EB1CE9FDE66074D20AA83 /* MJRefreshComponent.h */, 118F0C07C32E76EF6489509A4F07F50B /* MJRefreshComponent.m */, 1C7E38D158EEE0908532959AA8DCB439 /* MJRefreshConst.h */, 75AD8F0F482E2F585B2664D1635B4706 /* MJRefreshConst.m */, 8FF93E1E76B5ED1DA01941D734DB3531 /* MJRefreshFooter.h */, 0748670A15DF8EB5BA1C623F6B944876 /* MJRefreshFooter.m */, 9D8CA91B2544EA3008663944FBDDBB5F /* MJRefreshGifHeader.h */, C68244972029A1CB9C966D064AB6967F /* MJRefreshGifHeader.m */, 91BE41B89A55956B97FA90BA78DA1A4C /* MJRefreshHeader.h */, 203840BDEB077FAE0820685734949691 /* MJRefreshHeader.m */, 55C6E2980720E2A6CC91BB366BC6C7B2 /* MJRefreshNormalHeader.h */, 77D21DAC0D06E0D6BBF5A78CB767E815 /* MJRefreshNormalHeader.m */, 09B0C33CD96D9A514D7DB3610C6D2F6E /* MJRefreshStateHeader.h */, 613B23B6CFFCB99006B5893BE2C653A5 /* MJRefreshStateHeader.m */, EA3EFB292391C42C14DBF00E49C533EC /* NSBundle+MJRefresh.h */, 09517CD921BEB56F91C5FA7988170302 /* NSBundle+MJRefresh.m */, FB8C71D5C39C423BB61425ABDC60B40B /* UIScrollView+MJExtension.h */, 5B8B57244416BAE5506E83DFBCCE1F45 /* UIScrollView+MJExtension.m */, F76E37DE05B72E5ACE3CC2FEA524DC00 /* UIScrollView+MJRefresh.h */, 3BF627D8C19D8504043EEDE55963F4BA /* UIScrollView+MJRefresh.m */, 02FA95A68EC5AC5D4C787DD8DAA5C118 /* UIView+MJExtension.h */, ABF289602106E94D3F277B9C7F39FF7C /* UIView+MJExtension.m */, 1064728F3888D15A87C94E495EFE16CC /* Resources */, A723DA53CE13E2220EC96B75C92CBEDE /* Support Files */, ); path = MJRefresh; sourceTree = ""; }; 304EFC2FC2C7CF78255AFFCF0DB1A0DB /* RxSwift */ = { isa = PBXGroup; children = ( 542762FC821F584B23860CBBBAF13564 /* AddRef.swift */, 332E6B6D83344EA6F35D0681ADEAD1A7 /* Amb.swift */, C0B487DA905C4F895E0D126C1A062261 /* AnonymousDisposable.swift */, 6DB87787AD0F7333249395D1378FED63 /* AnonymousObserver.swift */, D06DCA6A0B6616240EC75F089FCB605D /* AnyObserver.swift */, EDA69717822ACCAAF367E9F539039219 /* AsMaybe.swift */, 2FA6CC0C558EDC9E6881C22246E9E059 /* AsSingle.swift */, A276DD5300A0CD1F004D67179921FE96 /* AsyncLock.swift */, 272D79A04BED524CBED07D2B4EC99B49 /* AsyncSubject.swift */, 59356F06A577C220AEBEDC900F94102A /* Bag.swift */, F384E81160731D07A8472A16F0EF1EFD /* Bag+Rx.swift */, 59B6B580BF9608405256B589F993BA72 /* BehaviorSubject.swift */, B10806D65630D3922CC4632E4B961170 /* BinaryDisposable.swift */, 4CE22F0C823232387AAE225F52265246 /* BooleanDisposable.swift */, 3BB1570E1320634AB029069C7CA04D40 /* Buffer.swift */, 495C0C539FEC03003AFB93904736DE91 /* Cancelable.swift */, 8BCCB28DE00E01DFCDBAFD966F967237 /* Catch.swift */, 18A31C1EE5C5A400DA67610DDECCBD49 /* CombineLatest.swift */, 280D08DD70717640589F62EB1F6FD19B /* CombineLatest+arity.swift */, E4051F928839DBFCB64F5087D54C40E3 /* CombineLatest+Collection.swift */, 52986B63E04FBCE90E31182FC836DF8C /* Completable.swift */, FF01C2ED8BF6578C8212FD3EAC90D3E2 /* Completable+AndThen.swift */, C10FF295CB8B9A0152FA38DF372846A4 /* CompositeDisposable.swift */, 26530B988040DFAD1200285B6F15D492 /* Concat.swift */, C7A200DBF260A1DD96FA33642A921C9F /* ConcurrentDispatchQueueScheduler.swift */, 1BCF16201BDE699E2122416B0D2FBD6E /* ConcurrentMainScheduler.swift */, 06147002706BB75CECECA91480E5C0E6 /* ConnectableObservableType.swift */, D2E1B85E14851EF923C2BD626BE22587 /* Create.swift */, EA47B9E0DFFD8D15F19F5033E63B9026 /* CurrentThreadScheduler.swift */, 23E72118C85ED536830E63BFEF402950 /* Debounce.swift */, 8F2CEBF6D286D3D808C8AB81A58AFF59 /* Debug.swift */, DA67E4E3F08A96BF5B2596ED0FC34196 /* DefaultIfEmpty.swift */, 7D4430407B638D271F51E5D3E920BB51 /* Deferred.swift */, A25F9545112FB4EA2545D303AC81CFB1 /* Delay.swift */, 770CC8138D39A339E5C8057EC2DD1F7D /* DelaySubscription.swift */, 8ACB6BCA51D81D396B134C596B0FB8C1 /* Dematerialize.swift */, C481BF3DA3A78A8E7F5176B802625CC6 /* Deprecated.swift */, 96BC1A1EF9084AA9223F0E5E094C6ABC /* DispatchQueue+Extensions.swift */, 36DC95F95B0777638C99DDB71789CF16 /* DispatchQueueConfiguration.swift */, B8DAA4F672011BE9BD20D53B1B64E479 /* Disposable.swift */, 49461972A7736ED5C10EA776E946211B /* Disposables.swift */, 23FEC5DC4D59BF5FF4FE6E5DE5CF64BC /* DisposeBag.swift */, F0758B65380EFC2497BC08F9A7E4F402 /* DisposeBase.swift */, DD1C4965E4534F094C7E5D0417A804AC /* DistinctUntilChanged.swift */, E74A11FE1DBDB7EA6BDB703F6B58F460 /* Do.swift */, 397F2D1B6C0CB8DEFA63186BB12618B2 /* ElementAt.swift */, 4497D98DB94E983018E6F6C2A4DBAF68 /* Empty.swift */, 0785C89934FE4C79E378F80B3EAA947F /* Enumerated.swift */, 04FE7430ECAF1467968440D35165D54B /* Error.swift */, 80A58779165736F60C355BBAAF75C027 /* Errors.swift */, 95CF89A127DC79551BAA8988F82BD1E2 /* Event.swift */, 20D269397D68987A4E3F571B01394F0E /* Filter.swift */, E415D1E19F77D7279C5F140411D69E67 /* First.swift */, 3E6B1DB2C5390EA79CEFCA5BAC709AFF /* Generate.swift */, 7A0ECA86834476786492EC1E52EB3E13 /* GroupBy.swift */, AE4600B7894931DEAFCABE02CD7AB268 /* GroupedObservable.swift */, 47F3ADD7515A562BFBBBB8888ADA3090 /* HistoricalScheduler.swift */, D76F782371F7D6EF97DE209867481EA5 /* HistoricalSchedulerTimeConverter.swift */, 95A5AC992E4B703AA08D0A815AAED779 /* ImmediateSchedulerType.swift */, 404B3E0121644CBA834D592A1EB4E364 /* InfiniteSequence.swift */, 87CE09E0A8341C183F59A37440D5153A /* InvocableScheduledItem.swift */, 593B68AD069BBE7957F7B4AE1E9F164A /* InvocableType.swift */, 096040CC61B2863666482591D451AA38 /* Just.swift */, F364B3A757735A453DB16E22F1A2D52A /* Lock.swift */, A5D6508C06EC007A57DBB3099AD5C617 /* LockOwnerType.swift */, E420D6F4781A96E122D216CBDA976615 /* MainScheduler.swift */, 954CE4894A117D9DA14AE5D47A6BAE88 /* Map.swift */, E417881CF3E530ABBE356AD806F81649 /* Materialize.swift */, 8C8ADE73FE10FA15ABAA1CB4FB6DAD62 /* Maybe.swift */, 14223DB3BAAFC3650B856F3ED4CA3293 /* Merge.swift */, 0D21901F32D924DB95300B2C650103E1 /* Multicast.swift */, 8F5E590482B741DD275B9A87B7EAF4DA /* Never.swift */, 20CECB0BD149F46C10733B4F2D2D92DC /* NopDisposable.swift */, 2B34630D7EB0A587B97952108987DB65 /* Observable.swift */, 63F72D0F593F82201EA482E15012B694 /* ObservableConvertibleType.swift */, 3EEF0C4EECF8525D00D4B8CF83ED7D3E /* ObservableType.swift */, 90107D958C65D510896483F2CB8060BA /* ObservableType+Extensions.swift */, 0C40C05B2CC135DAAF345CD1E46FE065 /* ObservableType+PrimitiveSequence.swift */, 5F34BA700CC37058B6A053DD142E65DB /* ObserveOn.swift */, 1003DE1B3A476A1579EADFC5DEE4DC09 /* ObserverBase.swift */, 834769F9B06E2DF4306C35CB6F967BAE /* ObserverType.swift */, 70876246376D5A1D2C4ADF75F307186A /* OperationQueueScheduler.swift */, 3CCA6ED6120DA770AB86F564EEAC46DB /* Optional.swift */, 17F817A4133AB7260E9700802CE54631 /* Platform.Darwin.swift */, 9C5AF229FF21FF8786BD3CDF9E0BD504 /* Platform.Linux.swift */, C7AF230FEF4F9203FE69CA75CAC56E33 /* PrimitiveSequence.swift */, B09E2CE4824B4A44FE923169E5B3934C /* PrimitiveSequence+Zip+arity.swift */, 30D68DEE1421B7ED892C4E0B33E9CC35 /* PriorityQueue.swift */, 8DC2D7127D27BDD70C5EC0A4FF96074D /* Producer.swift */, C1DC026D0D8F36437F6F9662A1805D0E /* PublishSubject.swift */, 502F0D4E9FE8FBC064C04982F146FE43 /* Queue.swift */, DEB5737B2DA2DF3423F29C0CBB7542DA /* Range.swift */, 5DF0A09A6DD2C0453F866A408223B631 /* Reactive.swift */, EAAE74B8E84AC1A31796559B46480858 /* RecursiveLock.swift */, 383DA86E343CBA75A7B8574AB75E1A1B /* RecursiveScheduler.swift */, 4914A9BB07C4CB2A6DDD5D0096A0BE87 /* Reduce.swift */, 8C5BD7D77FAF305B2C6E8F85C768FC42 /* RefCountDisposable.swift */, 2D6D95EA842EF7D1BFA6D8191AAE620B /* Repeat.swift */, F9B85640D4BCBBFE6662A71B6EE98AE9 /* ReplaySubject.swift */, A9A3DB0E144693D175CBA841C8B10601 /* RetryWhen.swift */, EC8A134B3C9407540FCDEC584183133D /* Rx.swift */, 44D999B360E6504760FF4A8DD2CB17A1 /* RxMutableBox.swift */, 351865278BBE506A2B088B8755AD46BD /* Sample.swift */, 7C5ABD7D168407FE39CC76E2B5D13784 /* Scan.swift */, EF04BEEB09DE1352D9F767144EF0FD50 /* ScheduledDisposable.swift */, BDC3EE36C7DE6D97F96B2B36B0E557C9 /* ScheduledItem.swift */, B32CA5B611266CA013631C5E362ACECE /* ScheduledItemType.swift */, 3A098FD7778F9B758BBC03971A34449C /* SchedulerServices+Emulation.swift */, BE49DD1F3CB80E0DCB3ED742E3DED62B /* SchedulerType.swift */, 99F328B2BEE043C872339B6E419D2095 /* Sequence.swift */, 0A81EC5AF3F05041AE6EB4193B2BFEE3 /* SerialDispatchQueueScheduler.swift */, F08868A7AD5BECFD90CC22A62671DC82 /* SerialDisposable.swift */, E127D4A1CC515D5A1562D756ADE9089E /* ShareReplayScope.swift */, 63C44514B1EC24A99AC7728B3503A038 /* Single.swift */, 70EBA339974E545CDC23F472B6A4BD3E /* SingleAssignmentDisposable.swift */, 0D231836443906AF664DE2289F32D387 /* SingleAsync.swift */, 0D305720CFFC3323166DEE86CAF9341B /* Sink.swift */, E018F18F377B9190734B1D9EB8160AD7 /* Skip.swift */, 30C22B352D9B240DC40532BF2241C498 /* SkipUntil.swift */, 4CF8C44E75B472322D8DC2B86B893ED9 /* SkipWhile.swift */, 861BD4AC5D5011049786E6352C62DC93 /* StartWith.swift */, 1D3CBDD7BD1351D05617EDFD0171017D /* String+Rx.swift */, 089E5120665D4B93D03D218D3D11C2F1 /* SubjectType.swift */, 137E979916631AD9ACADADEEEBBBAE5B /* SubscribeOn.swift */, C0112A7EDEEB67FF3C434A3F925DEA88 /* SubscriptionDisposable.swift */, 1DB825CD3DE4C8C88F1EB6E75E3046B2 /* SwiftSupport.swift */, CC94DB3E4DBB1ECF4FA33EBC9EAACFDF /* Switch.swift */, A0E83A69D9C8F0B40317DD90BF832D26 /* SwitchIfEmpty.swift */, 54D4C9E3C57DE8A35333AAD4C4E744A0 /* SynchronizedDisposeType.swift */, BE4CFCC4E9A965F95EE17202FBB5F310 /* SynchronizedOnType.swift */, 9946E97DCFB4572476564D33A05975E4 /* SynchronizedUnsubscribeType.swift */, 701196C1B8D5354D685D7DD627877FEA /* TailRecursiveSink.swift */, 1F80E196EA77F97C6AA8A3D2410BFE76 /* Take.swift */, 553ED5CCD42E1F512E1D9E9466E47D44 /* TakeLast.swift */, 5900BA1E16E8EB96D52F16B3133DD305 /* TakeUntil.swift */, 7BED0C58934B33DC41A1432BABD2EFA8 /* TakeWhile.swift */, 29C099BC759B6B4145E48DE537580E13 /* Throttle.swift */, AE9BA3DB7A5A422C613CFBFB2811FEA5 /* Timeout.swift */, 039485063DF511DC852BBCD65C474C9B /* Timer.swift */, BC7B9021F90F58D6E367BA8B4C9C2B14 /* ToArray.swift */, 5BCDD91A5C3CBCA65A1E0746F0ABC174 /* Using.swift */, 66AD8E23D6C4A5FFCFE4C066E700B676 /* VirtualTimeConverterType.swift */, 99692BFA25CF2437019950A2EFAE0291 /* VirtualTimeScheduler.swift */, 2A9BB47909385E153DEAF4820584660C /* Window.swift */, B00B2D1E5594681E1384A988B35D66F6 /* WithLatestFrom.swift */, 2F954956863F74706AA3A395F8401F14 /* Zip.swift */, A678D15F6F0F97A723B186CEA25EE5A4 /* Zip+arity.swift */, A088E5F2C74178B8F5D416C973214422 /* Zip+Collection.swift */, 7C6A2244A91617645C0390EDBBB4FD1F /* Support Files */, ); path = RxSwift; sourceTree = ""; }; 30849D5837BB3A25E52DB968699B62A0 /* Support Files */ = { isa = PBXGroup; children = ( B216F1C86A2BB68A807DD4CD8314351F /* Info.plist */, BCC1C0B2E36AC5340AC67AD9AD286793 /* ObjectMapper.modulemap */, 40C89EACDC9DB733AEDA2C1858349B05 /* ObjectMapper.xcconfig */, BC52351249DB2B467541996968B175A0 /* ObjectMapper-dummy.m */, 1039A64D7973971C931057162135E4B5 /* ObjectMapper-prefix.pch */, 0A332DD66083AD9F2BEB500FC7F9B60E /* ObjectMapper-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/ObjectMapper"; sourceTree = ""; }; 38BFD9FA65D37C0B02CF123BFDEAA695 /* Core */ = { isa = PBXGroup; children = ( 188117C123963623C4A723C6D908FDF6 /* ReusableKit.swift */, AC1DC57990281D4F45BE128AADCE4143 /* UICollectionView+ReusableKit.swift */, 5EAD58DE19CDA807C841C29CE9804B80 /* UITableView+ReusableKit.swift */, ); name = Core; sourceTree = ""; }; 45B00A8DA5873AD20582737CD95C2411 /* Moya */ = { isa = PBXGroup; children = ( D2FEDEA5B32B192432554BB4449C46F8 /* Core */, DEA91A40A855CC667C28E90C7BC1131C /* Support Files */, ); path = Moya; sourceTree = ""; }; 45BA95C8E036FDF76B829A05394F3326 /* SwiftyColor */ = { isa = PBXGroup; children = ( 01D96AEEF8ACF586BFC5351394E36F02 /* SwiftyColor.swift */, 53A5B24B0F63B17031A5156469414C11 /* Support Files */, ); path = SwiftyColor; sourceTree = ""; }; 487312B5947B5B410F4B2D74B97A64B4 /* Support Files */ = { isa = PBXGroup; children = ( D302B83E9CE0D2B43DFA34A03D0DBE2B /* Info.plist */, 32D7A430AD912A7D4AEE013B35DE2741 /* NSObject+Rx.modulemap */, 8D3F2B09E35E2D2EFA58CB9D9261E7D0 /* NSObject+Rx.xcconfig */, B8020BB1D97D4EA3246C9476D7044FE7 /* NSObject+Rx-dummy.m */, A6F0EAB300D0CE91C61EF3E29868A1D0 /* NSObject+Rx-prefix.pch */, 8533AEEE8CD59BA624CA9267423D28E5 /* NSObject+Rx-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/NSObject+Rx"; sourceTree = ""; }; 488B1D880370242DA6DAA1BC23EE213D /* Support Files */ = { isa = PBXGroup; children = ( 91425961AD55DFFE4C3BCB82A387076E /* Differentiator.modulemap */, 02242206E1BBE2B18A7CA031B7102D98 /* Differentiator.xcconfig */, C0DB238A8FB8D79A81E67CBB38ECF524 /* Differentiator-dummy.m */, 65CB9B0C5FAEB1D7382D6EDFA4938D81 /* Differentiator-prefix.pch */, 5E7C918776C8129D7372B1095C6E2BBD /* Differentiator-umbrella.h */, E9A37A86FC5231B8DFC0C17337CC40F5 /* Info.plist */, ); name = "Support Files"; path = "../Target Support Files/Differentiator"; sourceTree = ""; }; 4F4D1E519FEB89BA1386B9D98230B348 /* ObjectMapper */ = { isa = PBXGroup; children = ( 4D811B382CEAC4FADDA971AFE90AD50A /* CustomDateFormatTransform.swift */, EC29C744A1F98EEA2D40646969A57510 /* DataTransform.swift */, 655359FE97777A5E6376898C32918AD1 /* DateFormatterTransform.swift */, 0B86B4FEA51A1E51158BB9084C301356 /* DateTransform.swift */, 78C07D23AF9D0ED48A9FEB791BA9FE25 /* DictionaryTransform.swift */, E441DE19DC813E660F49CCA5AF0FA3F5 /* EnumOperators.swift */, 49F4BBE33D9DA445DD902A8A15991A56 /* EnumTransform.swift */, 236D0EC42AF18C98E4FFEB08963E7C51 /* FromJSON.swift */, 530479F817D071873ED5DC57B4372350 /* HexColorTransform.swift */, 88CD1219127C85AAD3C013FB79D9BA84 /* ImmutableMappable.swift */, 81B6FFA1A029CC85A9BD8A4847631CD7 /* IntegerOperators.swift */, F4851621D756478D43208A461240D7CF /* ISO8601DateTransform.swift */, 1E49107D99771943128C7B1A58B2781A /* Map.swift */, 7ADE1D2FF0526260DCE037584DAB5A0D /* MapError.swift */, 4C08FCDD6B32CEDDEA0C67B874CFF29A /* Mappable.swift */, 541CB7596A9F5575EA8684328858A044 /* Mapper.swift */, F3AFC24D86F7304F0B4AF511C9B9970A /* NSDecimalNumberTransform.swift */, 115F5111E88B41835988258D0B9D90F2 /* Operators.swift */, 42A26062E1F6F9B2A5BC508EA3D00288 /* ToJSON.swift */, 5DED5486ED70B440763840FA459F4698 /* TransformOf.swift */, F03D2DC1D0CBA5B7FB557A21D0DA5771 /* TransformOperators.swift */, F73232742568F2DC4E279896B22BA8A3 /* TransformType.swift */, 9979EBBDCDD9A7DDDB0725FEC97660E7 /* URLTransform.swift */, 30849D5837BB3A25E52DB968699B62A0 /* Support Files */, ); path = ObjectMapper; sourceTree = ""; }; 536713D0367D857983EAE8C88CBFAD20 /* TTRangeSlider */ = { isa = PBXGroup; children = ( C0B85CEF090EDD7737A7E0F1D0F0F169 /* TTRangeSlider.h */, A46A36F4BF38056EAA48578282F54B60 /* TTRangeSlider.m */, B86E459038ACF95C44868462505C56A6 /* TTRangeSliderDelegate.h */, 0E9D9F01EDD13FD4E5C0AB6FD5BB2A5A /* Support Files */, ); path = TTRangeSlider; sourceTree = ""; }; 53A5B24B0F63B17031A5156469414C11 /* Support Files */ = { isa = PBXGroup; children = ( B7FBCAED115C93A340B4D4817383226A /* Info.plist */, DE50048633BEA4A961DBEE8DC7631174 /* SwiftyColor.modulemap */, 7AA6F3E86B6FCC86A1E5A52F243EE56D /* SwiftyColor.xcconfig */, 7DA9FEB5DF32D81B067986FD382163B1 /* SwiftyColor-dummy.m */, 53FDE7E458AD9BF6993525EC0EC3CBFF /* SwiftyColor-prefix.pch */, 52A5AC3A3FC014CA05565755B1794774 /* SwiftyColor-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/SwiftyColor"; sourceTree = ""; }; 58F2F503951D4229783CFCA7DB8EFEEB /* Support Files */ = { isa = PBXGroup; children = ( BE521621242425215AB95C0D1F947998 /* Info.plist */, 63D809CF6C0676EAD17D2011895CB209 /* URLNavigator.modulemap */, 268594CEB578B4D18F4F1B30652F617B /* URLNavigator.xcconfig */, 504FC395E9A355EFA723008D25BE2E85 /* URLNavigator-dummy.m */, 6AC97A632E3388162A1E604E64EF8D3C /* URLNavigator-prefix.pch */, D783020743D1A18018EE614D78D965A2 /* URLNavigator-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/URLNavigator"; sourceTree = ""; }; 60E933F022A2CA3C626EFE6FCECD25C1 /* ReactorKit */ = { isa = PBXGroup; children = ( 9647D7ACC10E74465531ADEEFD4D2E0B /* ActionSubject.swift */, 83A11C513330652C7580864E6FF559BC /* AssociatedObjectStore.swift */, 1C74C02A940DA7E7A70BFF3EDB096E49 /* Reactor.swift */, 385C967E6D18858D5311CA87A4205434 /* ReactorKitRuntime.h */, 5F70D589B9E780A60A3716F46A4ECF0D /* ReactorKitRuntime.m */, 1A0CBF488AAC85E03EDD426C11452A46 /* StoryboardView.swift */, D71887F7534D1360E1CF05E79FDAC471 /* Stub.swift */, 3795E84704A9198DC6E04FB5A7227CCA /* View.swift */, 24DBE0B1E85B2506F4E5B030BBFE143F /* Support Files */, ); path = ReactorKit; sourceTree = ""; }; 620E4B9F95F286C23D5010BD6D3438BE /* Targets Support Files */ = { isa = PBXGroup; children = ( C517B73BB29B67C841CF338715E23F63 /* Pods-RxXMLY */, ); name = "Targets Support Files"; sourceTree = ""; }; 675A751295C96B00405E15E00041E422 /* Support Files */ = { isa = PBXGroup; children = ( DD77C22F0E17F82C01E907DC393087DA /* Info.plist */, 6F6066636CC572B64FC9681E44E91280 /* Result.modulemap */, 8A5ABA18A2D1A61A185ED12DF70B8F30 /* Result.xcconfig */, D4A33E97EE17B95FCE36DEBE7ACE03F3 /* Result-dummy.m */, FE2F015C7CCD3096659FF2450B75E15F /* Result-prefix.pch */, 57EDA4292816BC724EE611C648E430DA /* Result-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/Result"; sourceTree = ""; }; 712E1C09C3CA54FA0B661841A1FB2F97 /* Products */ = { isa = PBXGroup; children = ( A6BE7CE8E3CF10B0BE4713CA7CD3FDC9 /* Alamofire.framework */, EFD5B31121E42968628607F4CA11A4BC /* Differentiator.framework */, 924F8DE96A22311CC14038BB62C8997D /* IQKeyboardManagerSwift.framework */, 1E1DF457C00960360E254419271FA76F /* Kingfisher.framework */, B7B99FDB86B839402402CF4B9085F867 /* MJRefresh.framework */, 3E00F875EA38811C01C6F56B33214C42 /* Moya.framework */, 84E9E4B67AD852A6167EFE006AD2043F /* NSObject_Rx.framework */, 68051FD77C0407C9B24515E31E177222 /* ObjectMapper.framework */, B2C6C61F18ADC817A64446B3F214A416 /* Pods_RxXMLY.framework */, 195D6043A1C7C9E2F93F03BF5A6C765E /* ReactorKit.framework */, BC645F028499DF5A3CB986DD6D4358F1 /* Result.framework */, 68668B40B5E550006A59D5621EFB0561 /* ReusableKit.framework */, E02E53CE029ADA867E576C62B9054390 /* RxAlamofire.framework */, E9246ADE8730ABD21F1BBC59333EA31E /* RxCocoa.framework */, 97B60E643384171A74CED9D708E98F3D /* RxDataSources.framework */, D5B52B71645C13D27A3C4B3A58EA2440 /* RxGesture.framework */, 0971AB82F462A7C2F67101AD9407905F /* RxSwift.framework */, 4F5B061DB45E3912FBD1E11E6F1A842E /* SnapKit.framework */, A95B5537CC319932AD399F0660CB16D8 /* SwiftyColor.framework */, E915131DA668033B6756FDE94DD6C130 /* SwiftyJSON.framework */, FB6BED57753CCBC597942DFD41588338 /* Then.framework */, 2135AEA738411503ADADD4D2497AA4E6 /* TTRangeSlider.framework */, 91F4320128E8926FF21C0A92F70CFBBD /* TYCyclePagerView.framework */, B228A61D64CA44B4648977930AEEBF5F /* TYPagerController.framework */, 430B138602CD2D328367A37FEE11E65F /* URLNavigator.framework */, ); name = Products; sourceTree = ""; }; 7396FD9E9C585EB1A3E6449CEC17B0F1 /* IQKeyboardManagerSwift */ = { isa = PBXGroup; children = ( 8E6990B20E9EA105BBB88B21C57593B5 /* IQBarButtonItem.swift */, 507ED73099FD654BE7A82BBDAADE2C57 /* IQKeyboardManager.swift */, 12F89941B737495BB6B9D65D0A940759 /* IQKeyboardManagerConstants.swift */, BAB7E27C2C9842A03F3BD947DD8493A2 /* IQKeyboardManagerConstantsInternal.swift */, A6EC870FE5CE9FD966D23435C1DB6E0C /* IQKeyboardReturnKeyHandler.swift */, 76B7D152E72F3D0808E4186445E4FC9B /* IQNSArray+Sort.swift */, 2C114C6520E1FE3996B0B360BF559199 /* IQPreviousNextView.swift */, 87874701D9E81D433E42E8AAABDFFD7A /* IQTextView.swift */, 3F99F2A0C1CCF46EC645BA9E65F78200 /* IQTitleBarButtonItem.swift */, 155A351F30BA1128E99E067F81CB72B2 /* IQToolbar.swift */, FB352C9155E142C44FAE86885DBC3E62 /* IQUIScrollView+Additions.swift */, 80810E76C51AA5F19CE1D4B0D55E060C /* IQUITextFieldView+Additions.swift */, 1EF8AC35D0E53603F8A97DBBE9FA33E2 /* IQUIView+Hierarchy.swift */, 50E667F49AB3F41A361BA1B406783ABF /* IQUIView+IQKeyboardToolbar.swift */, 88CB6A2CAB2E3799803CD1C118F1F107 /* IQUIViewController+Additions.swift */, 6D94ED6DD481751BB6F57C5B6C60049A /* IQUIWindow+Hierarchy.swift */, 125AE60B5B1A09953287C270122642EE /* Resources */, B6F2514C869FD45712CBC40F05EEAC6C /* Support Files */, ); path = IQKeyboardManagerSwift; sourceTree = ""; }; 7A355B44A1746EE91ED56D1FB55506FC /* Support Files */ = { isa = PBXGroup; children = ( FE622F585E2DE18232A22FD2E069BE75 /* Info.plist */, 79B73C8686EF3B5668892169B223FA54 /* ReusableKit.modulemap */, B013E2FF6D44578E3E483B417504DD8F /* ReusableKit.xcconfig */, 5B4592368CE0F4DED0B9CAF915FC9215 /* ReusableKit-dummy.m */, 4F54F7C7B743ADE4D45AF0D405AA82BA /* ReusableKit-prefix.pch */, DD7CFB953733E5734A907460B2C40BCE /* ReusableKit-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/ReusableKit"; sourceTree = ""; }; 7C6A2244A91617645C0390EDBBB4FD1F /* Support Files */ = { isa = PBXGroup; children = ( F293CCDAB139CAA1824ACDC4BB8DE2EA /* Info.plist */, 9D4AFB0C0C4C623DA01CF63FA53AE13C /* RxSwift.modulemap */, 0A8EF0EB23267E967663F5FA5A8E7686 /* RxSwift.xcconfig */, 2D2C96C6D5355207398C46C9E66386BE /* RxSwift-dummy.m */, ECECB25BC65301EB453951C200FFCD22 /* RxSwift-prefix.pch */, 387296DC1983BFD653D558D678637B29 /* RxSwift-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/RxSwift"; sourceTree = ""; }; 7DB346D0F39D3F0E887471402A8071AB = { isa = PBXGroup; children = ( 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 02CB0B03CB75DEE740DEEDAFC9A4B34D /* Frameworks */, 9219C6A5FCFAFE30FB019F9ABF07FAE1 /* Pods */, 712E1C09C3CA54FA0B661841A1FB2F97 /* Products */, 620E4B9F95F286C23D5010BD6D3438BE /* Targets Support Files */, ); sourceTree = ""; }; 80B62B3BBDF1C43E982FC967D7AC3D71 /* RxCocoa */ = { isa = PBXGroup; children = ( B717B3469C2896926EF05EAF11498DA0 /* _RX.h */, 6FADD7C8C693834E93343EFB77D33AAD /* _RX.m */, 2534A262C08145CCB9C56D17DB32573D /* _RXDelegateProxy.h */, 2DEC3152A51E8F66A48451B7A4EC9704 /* _RXDelegateProxy.m */, E800F1732A0D7B4A6640BE266F14DDC8 /* _RXKVOObserver.h */, 957D3B23678E4698CC79A2DE9EA9F96A /* _RXKVOObserver.m */, B0D0CA0D4B6A0E2EB840F4E5D6C75340 /* _RXObjCRuntime.h */, 96BF47DDEF17AC72F8EBF9CF0EA9DC04 /* _RXObjCRuntime.m */, 0580BE8F6F974685A933A89D8A972BDA /* Bag.swift */, 4A42C76F3823E2EFA148B18E55C8D6B2 /* BehaviorRelay.swift */, 86FCD05DCA4B5988057772E478AA3B7C /* BehaviorRelay+Driver.swift */, C84F456A91C16DC64488D342C8BC5929 /* Binder.swift */, A962B38EA206CD0B32DEC9E6DC414479 /* ControlEvent.swift */, AAA5BE3862DD3A00CBB848A024A5E863 /* ControlEvent+Driver.swift */, 889059818A6183D31A0BDC7C6B981849 /* ControlProperty.swift */, CEC01AC2D958140E2A090D7B632F5567 /* ControlProperty+Driver.swift */, 05D420155A7835B81FCFE92D3FC75464 /* ControlTarget.swift */, BACE4EA986227C4D5F468E9FC4DB57BC /* DelegateProxy.swift */, 3DA4D4657D80847325EA00819DC11ACD /* DelegateProxyType.swift */, 2B88F0CF34C9C21FA5FACA8A0692AA4A /* Deprecated.swift */, F5367CAC7283271F0E12047EE1E54190 /* DispatchQueue+Extensions.swift */, 7C1A7F6D1EC5B670FE3CF2E00D21C2D6 /* Driver.swift */, AC294161F7CCD1280208FEC5EE254218 /* Driver+Subscription.swift */, 1530F4900EF841B8D75AB4AFA41076FB /* InfiniteSequence.swift */, AE8FF0C7C94471EB15B40751856B8D53 /* ItemEvents.swift */, B5A668A610822A5EACD5784C5EEE3E17 /* KVORepresentable.swift */, FC0E5E64017E2B544B9C4365BF0DBBF5 /* KVORepresentable+CoreGraphics.swift */, F79A80A6FF76E139A757BFA4110503E7 /* KVORepresentable+Swift.swift */, E03BFCA5D027189D74E5502CC0D58735 /* Logging.swift */, F7B06E614268F88A37528BE5B8A08933 /* NotificationCenter+Rx.swift */, A2B78D4FFAD19F5D58FA9B42B6E4F2FE /* NSLayoutConstraint+Rx.swift */, 3BEAA51E3D065BAC60F1B85CBB1F26D5 /* NSObject+Rx.swift */, 08A296320835E99AA5A0CED018A67A58 /* NSObject+Rx+KVORepresentable.swift */, D3F7DB8C08BAE7F26A0EE85DA33C3905 /* NSObject+Rx+RawRepresentable.swift */, F728D985BB2068A70834B0E454690223 /* NSTextStorage+Rx.swift */, AA67591D1C61133E2F626EA1C2E097FE /* Observable+Bind.swift */, 8E365162D0EA4A1CE302074F5079D99D /* ObservableConvertibleType+Driver.swift */, E91D5BAB4A332A7524C3625B57B61925 /* ObservableConvertibleType+Signal.swift */, CBD70508C00E38F1FE63CED0383CAFE6 /* Platform.Darwin.swift */, 9A82E326004C5937DB3D98AD29CEDFBA /* Platform.Linux.swift */, BEB35F8259524796939172BEB9769CB6 /* PriorityQueue.swift */, 346F9291AC0BA83FB7B3962FC7F73267 /* PublishRelay.swift */, BB9B39409869FB19DBBE9E21A282D0E1 /* PublishRelay+Signal.swift */, 1778148BC4EB285B75595B087B8FC931 /* Queue.swift */, 66C7719FC6A1EB8A6E7F08B0215685B3 /* RecursiveLock.swift */, 339F05AA6561AB55D443E3626724120A /* RxCocoa.h */, 98BFA9BCF11C38855F14FC8D710D4E95 /* RxCocoa.swift */, 809E8BEC46CF11685F9980428C93B446 /* RxCocoaObjCRuntimeError+Extensions.swift */, 64E69BA43FC5913EAFEEADEA145AFF1F /* RxCocoaRuntime.h */, 4C13EACC6D3C59A28B692EFB2DB14F6E /* RxCollectionViewDataSourceProxy.swift */, DA9C2D5FD4A6E72AF207648A0646DCCF /* RxCollectionViewDataSourceType.swift */, E3508AF1777C25C4BC5CE6002832B9BF /* RxCollectionViewDelegateProxy.swift */, 6B2C9F6C4EE9F9989261CF1F3418FC9E /* RxCollectionViewReactiveArrayDataSource.swift */, 4602CBFEB0FDD72D7A2FF806152CFC81 /* RxNavigationControllerDelegateProxy.swift */, 6FE766E158FAC90198034E79F61FB2CE /* RxPickerViewAdapter.swift */, 4284532690122048902F462ECE7D36F5 /* RxPickerViewDataSourceProxy.swift */, B97FA317AEF5791CA7299591278DDD85 /* RxPickerViewDataSourceType.swift */, 9096DCC6CC66370C372F38BF8C1E435A /* RxPickerViewDelegateProxy.swift */, BEF74C2451A7C311E3C89E75D4746DD4 /* RxScrollViewDelegateProxy.swift */, 0197B6C172230E3E4655E94AFC0EFD32 /* RxSearchBarDelegateProxy.swift */, ABBD78A9569D4426E15EAE838A7F4562 /* RxSearchControllerDelegateProxy.swift */, 9D01082F46B5E949B212F6A27CDEF6DF /* RxTabBarControllerDelegateProxy.swift */, C755463902805FE86A327D9AD0B26200 /* RxTabBarDelegateProxy.swift */, 7553CCC7443EE20DAEB595357CD7198F /* RxTableViewDataSourceProxy.swift */, 081D3C3389E6943F023464C1787FDC23 /* RxTableViewDataSourceType.swift */, 013A0244BEC843116DF0F443778476C9 /* RxTableViewDelegateProxy.swift */, E6BE51ED2DAF09F29A0DDADC0911D49B /* RxTableViewReactiveArrayDataSource.swift */, 8CF3671CDFCBADF26426E2EC9ED34424 /* RxTarget.swift */, CE2E4D442D52B0894ED51620B7A11E8B /* RxTextStorageDelegateProxy.swift */, 3771D2D60EF94F8BBA0E912C27A80D24 /* RxTextViewDelegateProxy.swift */, 90D0F930F0AE0A2471269302003B8A5F /* RxWebViewDelegateProxy.swift */, 4DCA82E849FDD3356D44EC7B8EE2BA84 /* SchedulerType+SharedSequence.swift */, 41DF1FD6C9D2BDC29A48989F838D1ECD /* SectionedViewDataSourceType.swift */, AF66AEF465B9C56A648DA16DBF3D2E4E /* SharedSequence.swift */, 20DFDFC714EBFF20FC56B6534173B322 /* SharedSequence+Operators.swift */, B840B6E9063F05A5F844D55042BC104D /* SharedSequence+Operators+arity.swift */, F570C010C51F2EDFA2B29E61A28988D2 /* Signal.swift */, 622801585769EF5EF95DF31C3FC639A9 /* Signal+Subscription.swift */, AE48FA13FBA4585E33723ED2572A6754 /* TextInput.swift */, D0C3B1C0259FBCD7A10525F73D370CF5 /* UIActivityIndicatorView+Rx.swift */, D69D61C111C8F4287DFE589110A04EA4 /* UIAlertAction+Rx.swift */, 58674873A60E033E010D6D00276BA742 /* UIApplication+Rx.swift */, 98B0262ADD4A92D4CCFE4F51BE3FD5D5 /* UIBarButtonItem+Rx.swift */, 9BBEE2DB22AD2B01DE2D742F08B660DE /* UIButton+Rx.swift */, 51BA22372DFDD297D4C486B391AE3C50 /* UICollectionView+Rx.swift */, 7E01EF7A9628B4C49AC12163B4FE6A91 /* UIControl+Rx.swift */, F28EC587F2484F4548370BCE6AA22C0C /* UIDatePicker+Rx.swift */, 64ED938103FC90F798F7A60F04981575 /* UIGestureRecognizer+Rx.swift */, DBF02AD8B77821AA7137D153FD7D26DC /* UIImageView+Rx.swift */, DE31A7BD0FFA90C66DA1DEBAD573E053 /* UILabel+Rx.swift */, D94169655CF6B1F44A9F5A11128DFBB5 /* UINavigationController+Rx.swift */, 610EF28DB806E2D0ECBF17409EB9EABE /* UINavigationItem+Rx.swift */, B599D41042A76B9DF98EB3822B3056BA /* UIPageControl+Rx.swift */, D819115F4043DA9F9126B97A2AAD84BD /* UIPickerView+Rx.swift */, 7C4B50F1957DB069477D1DF3B46EE040 /* UIProgressView+Rx.swift */, 7645EEB7C195C4CF487DFA28BA57D8E5 /* UIRefreshControl+Rx.swift */, EE68236572EEA4E94A38244743901FE9 /* UIScrollView+Rx.swift */, C2316CDD5367CCF5307F4C680E70DBBA /* UISearchBar+Rx.swift */, 53D0CE43D178809120E722B457D98361 /* UISearchController+Rx.swift */, 645CC45F2863AE7D2A6FB2A8C23E620F /* UISegmentedControl+Rx.swift */, 1B3623F446EB11D7EE2B1A505A7B1E5F /* UISlider+Rx.swift */, CDB96EFEBACCC377C2C1852E1187BDE3 /* UIStepper+Rx.swift */, 4773B687AFC9012C6545F9C30E80356F /* UISwitch+Rx.swift */, 9707233113E3D0E414A32F652E88D1D8 /* UITabBar+Rx.swift */, 25A0BA6CC0B9439AAC6261DC60D7AE1C /* UITabBarController+Rx.swift */, BE4D4A9C4DD3E20DEF7AD0D414C67442 /* UITabBarItem+Rx.swift */, AFBBB6581E245F03FD7746F2B3F6838A /* UITableView+Rx.swift */, 7088D6C1ADBF5BBD12CFF84A0E220FA2 /* UITextField+Rx.swift */, F6367B989A05CB68EFF4B1822D8DB33E /* UITextView+Rx.swift */, 8546B56E228E6A78E32833D60BB6D648 /* UIView+Rx.swift */, 536B3E455CFA43C1E70A9AD9ACF3CD69 /* UIViewController+Rx.swift */, AE7697FE2AC8BF4148D6676FBC1B5A23 /* UIWebView+Rx.swift */, D8FDDBEBD3900E6B6B3DE7D9D96C0CFA /* URLSession+Rx.swift */, 22A086CB1B582E984EBA7863AED9A385 /* Support Files */, ); path = RxCocoa; sourceTree = ""; }; 849E14A0A0FF8260C7A696605813B350 /* Support Files */ = { isa = PBXGroup; children = ( F1AC2191C0BA5AC761428DAD97E25B05 /* Info.plist */, 2FD006214C2E7016C83E906BBB4CD624 /* Then.modulemap */, EC5D50260AEE66CC8FD97C3CF9E10E7F /* Then.xcconfig */, D6AD0538BC7F97DC3694296C53DBE9C8 /* Then-dummy.m */, C5A28FC784D68445E20B42FEE607E770 /* Then-prefix.pch */, F71B53FF12451C5503271983A3FE932B /* Then-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/Then"; sourceTree = ""; }; 91854DC425805DA1171104E85059B15F /* Kingfisher */ = { isa = PBXGroup; children = ( 20735E206ED10BEE6B25EC79F4DB6BFA /* AnimatedImageView.swift */, 74AE307751AFEBCC18F1E034FE08A05F /* Box.swift */, 76C763EC2CA9D13E7E93A029686393D1 /* CacheSerializer.swift */, 4D3E83D571BF2E77162C09F72FD66D3C /* Filter.swift */, 9FDFBC7B8CD4E93779C4C76ACF38B06D /* FormatIndicatedCacheSerializer.swift */, 52D36179922B35EB5035CA039865F6B7 /* Image.swift */, CC6A0E89EEA59CA5659D2EED0C773FD1 /* ImageCache.swift */, FB757B34331F94A8CE2ABFE0AC0050D4 /* ImageDownloader.swift */, B48B219E8CC99707DBEA39F8FF5389AA /* ImagePrefetcher.swift */, CB17D569CE831A4F4E00E2FC816EBBC9 /* ImageProcessor.swift */, 56FC32B26F57769DE2EFE454C78CC62C /* ImageTransition.swift */, BE96693365ED0583573F43D8F5B57187 /* ImageView+Kingfisher.swift */, 3D450DFC0A437B3075E6E68C0FA1A2B3 /* Indicator.swift */, 6100112F9C1D863256E650CDE2B078D9 /* Kingfisher.h */, CC6B9B2770629FBEE8FF3829E91ACE5A /* Kingfisher.swift */, 59830F33473DBCA365D965B7747764A8 /* KingfisherManager.swift */, B28CBB9A2FC6BD76A7188D520046176D /* KingfisherOptionsInfo.swift */, F68D0C4E903F4B9E77676032554A5278 /* Placeholder.swift */, C5BFA257EAD55FA150B56C6B292EAA44 /* RequestModifier.swift */, 5B95FDEC37C94B6EBD089F1AD6038F94 /* Resource.swift */, 348D24C7C0138AD0B581B484EA58AD8B /* String+MD5.swift */, 0D8A0052E8AA49DDD883B85DEA3F90EF /* ThreadHelper.swift */, CF47A0081987D5825B05757F83159AE9 /* UIButton+Kingfisher.swift */, AF35B728E3D11CF36741264854AEE4DB /* Support Files */, ); path = Kingfisher; sourceTree = ""; }; 9219C6A5FCFAFE30FB019F9ABF07FAE1 /* Pods */ = { isa = PBXGroup; children = ( 94AFD9A0BE692417B5A84BF31B1763F3 /* Alamofire */, FE7BFC8454D904E1F9A200B5AA47553D /* Differentiator */, 7396FD9E9C585EB1A3E6449CEC17B0F1 /* IQKeyboardManagerSwift */, 91854DC425805DA1171104E85059B15F /* Kingfisher */, 2A11EEEC2D225026B3E7D7223F9F7A6F /* MJRefresh */, 45B00A8DA5873AD20582737CD95C2411 /* Moya */, C26864CA1ECB3E70EBF7A7DC22D08C02 /* NSObject+Rx */, 4F4D1E519FEB89BA1386B9D98230B348 /* ObjectMapper */, 60E933F022A2CA3C626EFE6FCECD25C1 /* ReactorKit */, A6BD63DC5D5D61FEFD9C681CFDA5C833 /* Result */, AC1EB74202399251F1E67DFED51A6FF0 /* ReusableKit */, 00B445FBF5C55D58619DD22ADD326E0B /* RxAlamofire */, 80B62B3BBDF1C43E982FC967D7AC3D71 /* RxCocoa */, CBCC60F9772D804FC0E251BD1EB78034 /* RxDataSources */, B53A238D42B5CB5510AEA87118ED8D68 /* RxGesture */, 304EFC2FC2C7CF78255AFFCF0DB1A0DB /* RxSwift */, 013689684E64F7EE37E9EED321657B7F /* SnapKit */, 45BA95C8E036FDF76B829A05394F3326 /* SwiftyColor */, A0C41F30E953A47452B5187BA80452B2 /* SwiftyJSON */, B51488D131FEEE7D671B05C2D36AFA33 /* Then */, 536713D0367D857983EAE8C88CBFAD20 /* TTRangeSlider */, B56A5B65D45C38D24EB740EBAD49DC83 /* TYCyclePagerView */, E0846B1B4A46262524A31FD8066ED283 /* TYPagerController */, E7BA26169D457678D84E11AE5BDBDE6A /* URLNavigator */, ); name = Pods; sourceTree = ""; }; 94AFD9A0BE692417B5A84BF31B1763F3 /* Alamofire */ = { isa = PBXGroup; children = ( 7253973D128A903FF19D65F0A9DB9316 /* AFError.swift */, 1CD95565DA23CF770293505264420AE0 /* Alamofire.swift */, 74515FDED92421AF135AF3C0B8B08A1B /* DispatchQueue+Alamofire.swift */, DC30C48A2548E77051CB43345238F5E4 /* MultipartFormData.swift */, DC3268ED5E6ABB0ABCDEEE48CE0CD78F /* NetworkReachabilityManager.swift */, 187ACC537834C3D22473C47D21364850 /* Notifications.swift */, 5EEFED166A077BBCCE208D34F71B616C /* ParameterEncoding.swift */, B8D62DD162002F9C56F1C29692DB9FB4 /* Request.swift */, B2E6EA6ADCEAC27A4822132EBAC46122 /* Response.swift */, 720B12F497653C86DA2AF27BCCDEFC2D /* ResponseSerialization.swift */, 812C7F916BBBCD2970C245989B39CD1B /* Result.swift */, 83B89463432FAA55975050E17F0E6B42 /* ServerTrustPolicy.swift */, 80DD88365EFE5E0A8E363300ADCE2DCC /* SessionDelegate.swift */, 95F766DA63DDEB880E468218435A871E /* SessionManager.swift */, 2CE37D8A7C1180573189B3F4D4D5D2A0 /* TaskDelegate.swift */, 7FF0A342E014C442F7C7474857AF5961 /* Timeline.swift */, E7023AE71AB1962FA73C0D900E6564BE /* Validation.swift */, D14EB94F4E9A069FF4BDEECDA5906685 /* Support Files */, ); path = Alamofire; sourceTree = ""; }; A0C41F30E953A47452B5187BA80452B2 /* SwiftyJSON */ = { isa = PBXGroup; children = ( 4DBD4CA78A84FE60982AF0780EC62DD2 /* SwiftyJSON.swift */, B77159DF4E517929A1AF172DBA71E823 /* Support Files */, ); path = SwiftyJSON; sourceTree = ""; }; A6BD63DC5D5D61FEFD9C681CFDA5C833 /* Result */ = { isa = PBXGroup; children = ( 36C41A4EDAFACF2FCF4214E703CD5C47 /* Result.swift */, 6A8E64555EB2FE7D579467AABA52EB26 /* ResultProtocol.swift */, 675A751295C96B00405E15E00041E422 /* Support Files */, ); path = Result; sourceTree = ""; }; A723DA53CE13E2220EC96B75C92CBEDE /* Support Files */ = { isa = PBXGroup; children = ( 5D1AFD41323E3922E91E58F1C828C74E /* Info.plist */, EC766D153E5F9EEED025A6A61E553812 /* MJRefresh.modulemap */, DB2A4AE8B923CFBB0D0531CB91C712CE /* MJRefresh.xcconfig */, D52211FFC97D4BB7CF97DA504F823D77 /* MJRefresh-dummy.m */, 8FF79B88AE1A71885D5E17758DB372B4 /* MJRefresh-prefix.pch */, 78266012E1D0050485CA537DD6F66FF5 /* MJRefresh-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/MJRefresh"; sourceTree = ""; }; AC1EB74202399251F1E67DFED51A6FF0 /* ReusableKit */ = { isa = PBXGroup; children = ( 38BFD9FA65D37C0B02CF123BFDEAA695 /* Core */, 1B1EA4443A2498E941CD029F27E9639D /* RxSwift */, 7A355B44A1746EE91ED56D1FB55506FC /* Support Files */, ); path = ReusableKit; sourceTree = ""; }; AF35B728E3D11CF36741264854AEE4DB /* Support Files */ = { isa = PBXGroup; children = ( E00D0BE5D24B8BDE0B2711827A346D65 /* Info.plist */, B0E8C13296CD0445ED73183C8774BA83 /* Kingfisher.modulemap */, B6DD7C62D3975725987453F154E89E03 /* Kingfisher.xcconfig */, 347D8CF191232FE2DBCADBECC8AB9A5F /* Kingfisher-dummy.m */, 5385AD0D889B0E809D292504198A4D49 /* Kingfisher-prefix.pch */, 36B939D525A7B01D959ECFCBDE9C0550 /* Kingfisher-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/Kingfisher"; sourceTree = ""; }; B51488D131FEEE7D671B05C2D36AFA33 /* Then */ = { isa = PBXGroup; children = ( 97927D56FAC9E735B7340A75B06C0600 /* Then.swift */, 849E14A0A0FF8260C7A696605813B350 /* Support Files */, ); path = Then; sourceTree = ""; }; B53A238D42B5CB5510AEA87118ED8D68 /* RxGesture */ = { isa = PBXGroup; children = ( 87C5FAB0B3B58A21449A74F3BE0722D4 /* GestureFactory.swift */, 9CD9A0FE14A09A30BF80B2ABA5E19488 /* RxGestureRecognizerDelegate.swift */, E6D2C080BD70A34F38B01F04F8C066D6 /* SharedTypes.swift */, 3B157B779C8184526F0932ECBA9879A9 /* TransformGestureRecognizers.swift */, A5C87EF44982A4D3803D88EB9983E52D /* UIGestureRecognizer+RxGesture.swift */, 500A7C34BE698156B2539B1AFD8A1AF3 /* UILongPressGestureRecognizer+RxGesture.swift */, B3F4C6BA01C9A3737D53D5029569BD25 /* UIPanGestureRecognizer+RxGesture.swift */, 569E77B7F0029A0A2087DE259C07DB22 /* UIPinchGestureRecognizer+RxGesture.swift */, 4F707D328F3FE6B0BA81F88374DC441C /* UIRotationGestureRecognizer+RxGesture.swift */, FD5F22C193A3AD6545BDE4B91729668D /* UIScreenEdgePanGestureRecognizer+RxGesture.swift */, 59330F519A185D61B0CDC18A2BC8BEF7 /* UISwipeGestureRecognizer+RxGesture.swift */, 589D959893A1EF698394AAC806F18758 /* UITapGestureRecognizer+RxGesture.swift */, 0F46744125C99B20BDE8928C9D91767D /* View+RxGesture.swift */, DEDDC2BCC376C172337F9B2E2B10E662 /* Support Files */, ); path = RxGesture; sourceTree = ""; }; B56A5B65D45C38D24EB740EBAD49DC83 /* TYCyclePagerView */ = { isa = PBXGroup; children = ( 4FC00E6B5EA00A0D7CA1DC61742ABA25 /* TYCyclePagerTransformLayout.h */, C47049CF48166B10EF910F23995AEC84 /* TYCyclePagerTransformLayout.m */, 01974B7A9277DCB1555881FFD8B7A8E1 /* TYCyclePagerView.h */, E1D44E21411F23F3505F31B444A1A104 /* TYCyclePagerView.m */, A93A51F7564EC64C94864C9A11DF7A0D /* TYPageControl.h */, E99B4913C9AF498445CF9BB785FFBFFF /* TYPageControl.m */, 0065A8C3C82D11C680574000E0D0A173 /* Support Files */, ); path = TYCyclePagerView; sourceTree = ""; }; B5FA546B46ADE3CF57779910E708C19D /* Support Files */ = { isa = PBXGroup; children = ( 8503DD41469B8F78519D7D0B98DBEE66 /* Info.plist */, 1CB7A49991CF5D7E632585BCFD130C56 /* RxDataSources.modulemap */, A222328DE93D544E711805351EB49909 /* RxDataSources.xcconfig */, E031F5913ADD2E4B2FE376BDDB99068C /* RxDataSources-dummy.m */, 3BEF595F5CDFC591D3F78073BECD08C6 /* RxDataSources-prefix.pch */, B9B6B63EED3B0A8A6802499D6992A2CE /* RxDataSources-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/RxDataSources"; sourceTree = ""; }; B6F2514C869FD45712CBC40F05EEAC6C /* Support Files */ = { isa = PBXGroup; children = ( 62DDFD5098F37CBDB331E2282F8DEB42 /* Info.plist */, DE2A6BA18853C4598D21FDBBD902BD32 /* IQKeyboardManagerSwift.modulemap */, 8D9FE6C7F2AC2C3319C58D4272FD52C1 /* IQKeyboardManagerSwift.xcconfig */, 51312E298DD4FE8C24D45F0F9149E1EB /* IQKeyboardManagerSwift-dummy.m */, 95E7A514BFE979A1185DE2AD4CF665AB /* IQKeyboardManagerSwift-prefix.pch */, 973B74E176AD01454BE984AC4A7136A0 /* IQKeyboardManagerSwift-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/IQKeyboardManagerSwift"; sourceTree = ""; }; B77159DF4E517929A1AF172DBA71E823 /* Support Files */ = { isa = PBXGroup; children = ( 16A6BD5743A5A18E98F1E4D0B05E9282 /* Info.plist */, 712CA70976070E14072608793D09319A /* SwiftyJSON.modulemap */, 4E295D0CB7D2F465413AC6086CBF8C49 /* SwiftyJSON.xcconfig */, 4DD33D08EA36F2C8861A36D8E15B3610 /* SwiftyJSON-dummy.m */, 8F3B9C1AE8FD54707F43CF28561D8FF7 /* SwiftyJSON-prefix.pch */, C8B8D267B8E5E2A789EBA75CCFF121F2 /* SwiftyJSON-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/SwiftyJSON"; sourceTree = ""; }; C26864CA1ECB3E70EBF7A7DC22D08C02 /* NSObject+Rx */ = { isa = PBXGroup; children = ( 4896403B4FA72B445E17270A138939D3 /* HasDisposeBag.swift */, 7A8F833CA4526A1FF65CC394962AD92E /* NSObject+Rx.swift */, 487312B5947B5B410F4B2D74B97A64B4 /* Support Files */, ); path = "NSObject+Rx"; sourceTree = ""; }; C2E4BC97956884C852E7E3676114F4CD /* Core */ = { isa = PBXGroup; children = ( 14F1C5DE23CD40C00D675D81FDE24A5E /* RxAlamofire.swift */, ); name = Core; sourceTree = ""; }; C517B73BB29B67C841CF338715E23F63 /* Pods-RxXMLY */ = { isa = PBXGroup; children = ( 9547423EEA761582EF5FDB90684A1EE8 /* Info.plist */, 36F99765559218EBDA04ED80AEF34EB9 /* Pods-RxXMLY.modulemap */, AE8EA095AFC8941AABB28F7469B54C94 /* Pods-RxXMLY-acknowledgements.markdown */, 0545D09F28CBBEE994F089B68636507A /* Pods-RxXMLY-acknowledgements.plist */, 85302FAB5FE773C188E3758D45ABFF83 /* Pods-RxXMLY-dummy.m */, 14D38200F2B2C8E328B6E393A63B99D2 /* Pods-RxXMLY-frameworks.sh */, E8D6DDBD1576F25B98F7059832A190A3 /* Pods-RxXMLY-resources.sh */, 17CB2FA1178A4F2E225AE19BEFEB3B81 /* Pods-RxXMLY-umbrella.h */, 716F007B040C0CDD77F14CF5209FEE01 /* Pods-RxXMLY.debug.xcconfig */, 3F697E60BD60ED1A4604DA7BF8581AB1 /* Pods-RxXMLY.release.xcconfig */, ); name = "Pods-RxXMLY"; path = "Target Support Files/Pods-RxXMLY"; sourceTree = ""; }; C87EF0D2F5E72ED8D8AE2A78F9590FBF /* Support Files */ = { isa = PBXGroup; children = ( 223F91E90B67ADF004A7E4543864C0E4 /* Info.plist */, 3E96DBA0FB443524D03EAA0B1907D6DF /* SnapKit.modulemap */, 82C7B1F1647EBCD9A81E7A2788701631 /* SnapKit.xcconfig */, 5E257A033C34CB083326378E455CBA7C /* SnapKit-dummy.m */, F01E5E91EC940C05D243E33611C9CA5C /* SnapKit-prefix.pch */, D2D55E77F429A861A4AEA5872801C81A /* SnapKit-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/SnapKit"; sourceTree = ""; }; CBCC60F9772D804FC0E251BD1EB78034 /* RxDataSources */ = { isa = PBXGroup; children = ( 6E8E6375D3923532919D7663A2689D73 /* AnimationConfiguration.swift */, 0BF26D84DAD5A55C5E5BE6A0660FAEEF /* Array+Extensions.swift */, 85B5D2C3BA32F9BC3AA7C975B97F6A50 /* CollectionViewSectionedDataSource.swift */, 591CADBCCC57E88AE178D78B8606C938 /* DataSources.swift */, B9CFFF8A56873944159E6B04DBAD45BD /* Deprecated.swift */, 2FF3BD6C6FED1A57B6B88C3F0EFEB6A3 /* FloatingPointType+IdentifiableType.swift */, 63CA5A419783AFF7463EA6144B0C1E36 /* IntegerType+IdentifiableType.swift */, 9F86ADA13F9525995BA4102D3E1950B2 /* RxCollectionViewSectionedAnimatedDataSource.swift */, 7897B0DA6B24D6557C87D44F527163A2 /* RxCollectionViewSectionedReloadDataSource.swift */, 84378AB9BAB98B7273235CFA0707DE2D /* RxPickerViewAdapter.swift */, 1599175E64562E7F65DC8ECDF4F4E69D /* RxTableViewSectionedAnimatedDataSource.swift */, AA64CC4D23BCB33D713381874C6A93C9 /* RxTableViewSectionedReloadDataSource.swift */, 5E6586D8B7FB36BDE9E96FE450F51E98 /* String+IdentifiableType.swift */, 2F1C6F4433BDCCAB44D788A1B10A5011 /* TableViewSectionedDataSource.swift */, 53647AFE918247CC408B6EF13349D614 /* UI+SectionedViewType.swift */, 42F0AF2A7F102777266AB6FFCA689615 /* ViewTransition.swift */, B5FA546B46ADE3CF57779910E708C19D /* Support Files */, ); path = RxDataSources; sourceTree = ""; }; D14EB94F4E9A069FF4BDEECDA5906685 /* Support Files */ = { isa = PBXGroup; children = ( 4761B90E528EAB72D76DD1A76777354E /* Alamofire.modulemap */, 9855A9C6CDDA35414C161F75BED8B6F8 /* Alamofire.xcconfig */, A82133367E269E7AA71B9DB6052EFEEB /* Alamofire-dummy.m */, 08EF34F0793697F59142C4D8F3A0D159 /* Alamofire-prefix.pch */, 21805A4E1E5079259A7A86A6D073E71B /* Alamofire-umbrella.h */, 2D9BD6B28993FCB68120C1EE96813B72 /* Info.plist */, ); name = "Support Files"; path = "../Target Support Files/Alamofire"; sourceTree = ""; }; D2FEDEA5B32B192432554BB4449C46F8 /* Core */ = { isa = PBXGroup; children = ( CEA052CCABE96871EBBAFD7D6C959A06 /* AccessTokenPlugin.swift */, 66B23F5CD12DC047D454DFB36DAF64B7 /* AnyEncodable.swift */, 70459A28397D1E419B4B3394FA8435F3 /* Cancellable.swift */, 4CB47246A17CA14228A79384E13C42EC /* CredentialsPlugin.swift */, 74A0AC039E269E338928C0D25F4ABC01 /* Endpoint.swift */, 298536EB0ACF540DC4DC34F019837D74 /* Image.swift */, DD71B5E50804962A47932EB53600027A /* Moya+Alamofire.swift */, 2F9BB5EBA26D7BF9CA0C3B21B1AAD9EE /* MoyaError.swift */, 0049E24D8DFDDC0E246DC35576E7DCFB /* MoyaProvider.swift */, 1555063617BFE21D0E44B5255EA230FE /* MoyaProvider+Defaults.swift */, AB53371318D8626F8F670FACB15EC0F0 /* MoyaProvider+Internal.swift */, E5D465BD6EB72259B11615DB9761D3DB /* MultipartFormData.swift */, BDA8654A5097FB77F91981EC742DE882 /* MultiTarget.swift */, 1C02566F282B7374FFE638BE27D65EBC /* NetworkActivityPlugin.swift */, 6E3539D710126BA8CE5B7B6A9EBF994E /* NetworkLoggerPlugin.swift */, 125CF19DCF28A39EC761EA281EBBC44B /* Plugin.swift */, DB7464D95C191D51D7944010316440FF /* Response.swift */, 08D8E58342C1CDB4D227FD9491D0E369 /* TargetType.swift */, F4F172EFA622B78B8E948E1F153B1BDD /* Task.swift */, 088517E9B0F7ED44CFEB3E14F7379BF5 /* URL+Moya.swift */, D85A799653D3BC7BA7BDB6BD691336F8 /* URLRequest+Encoding.swift */, ); name = Core; sourceTree = ""; }; D5050FC1A785C83B3ADD912414E51051 /* iOS */ = { isa = PBXGroup; children = ( 1C719E3ABA8B6DE4B326AD2FF6313BA2 /* CFNetwork.framework */, 313319FF55255C3736F46998BCDF7F8A /* CoreGraphics.framework */, 6F10EACF1B2D7C5A4C670634756973B3 /* Foundation.framework */, 7481193578343285EC8F430B736D1314 /* QuartzCore.framework */, 0E5F9481F27654B03C1A5A056D8909FB /* UIKit.framework */, ); name = iOS; sourceTree = ""; }; DEA91A40A855CC667C28E90C7BC1131C /* Support Files */ = { isa = PBXGroup; children = ( 8B3B388CB20702989EAED439666B6E88 /* Info.plist */, B68C41252797A96DC3101FEF24BD28A4 /* Moya.modulemap */, 28FD4835CF32D5E0B1A2A3753A09DBB0 /* Moya.xcconfig */, 8C6994BF34E50D97EB111CDFB02DA22A /* Moya-dummy.m */, 465C09D0B89915A2BF9E8EE9623D1080 /* Moya-prefix.pch */, 737DA5252745C9E3883929C8A145E22E /* Moya-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/Moya"; sourceTree = ""; }; DEDDC2BCC376C172337F9B2E2B10E662 /* Support Files */ = { isa = PBXGroup; children = ( E75CBFE107A4EA81EDD844CA7F371A0B /* Info.plist */, 8065BEF90C7FDDBD3D7FB5E732D08AD7 /* RxGesture.modulemap */, 41B3ACB613105C496C9AA802C306A439 /* RxGesture.xcconfig */, BE0C2F5C5C4F8CD822C21482EBE49DD8 /* RxGesture-dummy.m */, C70DA4143C75E4B49245F0F990AF117E /* RxGesture-prefix.pch */, E010BA57C8A6D94C6A65D63FA871BFCB /* RxGesture-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/RxGesture"; sourceTree = ""; }; E0846B1B4A46262524A31FD8066ED283 /* TYPagerController */ = { isa = PBXGroup; children = ( C0155AA3020891E25233E5B541EF2063 /* TYPagerController.h */, 1F5B9A2F08507775E806FE12ED90BAF6 /* TYPagerController.m */, 0E5085F3C34DD0D51CCDC43343115505 /* TYPagerView.h */, 6C82E28899508F47FDD43F9C7D4722F0 /* TYPagerView.m */, C5B39EAE40FAC88E85E0EDD5108794F0 /* TYPagerViewLayout.h */, 1BFCDA5713A7916617594A7D5B7BB187 /* TYPagerViewLayout.m */, 0F27A8AA53C280B6DF033E2515D7A2E9 /* TYTabPagerBar.h */, 8E49B1A15261BE86B3137451B245C6BE /* TYTabPagerBar.m */, D609A4B215C782073049E462F652D85D /* TYTabPagerBarCell.h */, 07A6E49BDE2308DCA0050930CA050850 /* TYTabPagerBarCell.m */, CD67BEEE3096E3E2BD09DF6158788A6E /* TYTabPagerBarLayout.h */, FA64F8A7EA7F1D019BA4637FA772850D /* TYTabPagerBarLayout.m */, 88860B4DEDC2F32A5AC3E06DD5F534B0 /* TYTabPagerController.h */, 24B87BC6C657291DEE2644EFCB9CA341 /* TYTabPagerController.m */, 22B5011A39DF5849503132CEE075CC1E /* TYTabPagerView.h */, BE113770F3941380D55A0F946E7431DE /* TYTabPagerView.m */, 19AF7A80FF7FACF9DA96ECF7C6C84AE6 /* Support Files */, ); path = TYPagerController; sourceTree = ""; }; E7BA26169D457678D84E11AE5BDBDE6A /* URLNavigator */ = { isa = PBXGroup; children = ( 084AF2E51A268DBF8C3B947B137318FC /* Navigator.swift */, 7A4923E32C7795B1406F85C0D769B8F1 /* NavigatorDelegate.swift */, 5A310736267696B70A17786FD9CE22A3 /* NavigatorType.swift */, 2F8F22838BEB780C855A8033AE7ED2A4 /* UIViewController+TopMostViewController.swift */, 2988A2FCCBF291E4BEAC7F8D9AD45A9F /* UIViewControllerType.swift */, 28C85F1DBF1BC0BBAE450D1FBEA54A9F /* URLConvertible.swift */, DB98DBDF6250E6EDD9D4711CCA72CDE3 /* URLMatcher.swift */, 49DA791DE895721812C9EB83583324F0 /* URLMatchResult.swift */, 2E647F479FFCF502B2E7DF9A363BE038 /* URLPatchComponentMatchResult.swift */, 306015976D0F37EFAC711062A258D8E9 /* URLPathComponent.swift */, 58F2F503951D4229783CFCA7DB8EFEEB /* Support Files */, ); path = URLNavigator; sourceTree = ""; }; EEBFA1F60BB4035823034A2E49888E19 /* Support Files */ = { isa = PBXGroup; children = ( 89BBA936343137BAF519218BA7905C65 /* Info.plist */, E8D611801824EEACA86E5FF945882746 /* RxAlamofire.modulemap */, 9D52DB027375B4BF6987825A6BF5751B /* RxAlamofire.xcconfig */, 34903204D85860DD93734C3981A9900D /* RxAlamofire-dummy.m */, DE17B34AF63AF3BB97075A6D295FDCB3 /* RxAlamofire-prefix.pch */, A93AB57B4326570AC0951E305DD04B1F /* RxAlamofire-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/RxAlamofire"; sourceTree = ""; }; FE7BFC8454D904E1F9A200B5AA47553D /* Differentiator */ = { isa = PBXGroup; children = ( A7F1EA6B598A844B871DB54C2C1C197D /* AnimatableSectionModel.swift */, 83BF88F0362AAB340061528783A19B27 /* AnimatableSectionModelType.swift */, A32996D04BF42DF4610DB92306B85695 /* AnimatableSectionModelType+ItemPath.swift */, C0764078D276516AEF879D67E95D225E /* Changeset.swift */, 47C651CFF068171CF1C1237A69E7D99A /* Diff.swift */, D3869DF0902D254BD40D6B02E11A8B52 /* IdentifiableType.swift */, DA8C0C17C65F493BBDD9F7C413C2C378 /* IdentifiableValue.swift */, 459BCD44D36BC348042325F44BD78250 /* ItemPath.swift */, C60A46D85400354A02A72584F53237D8 /* Optional+Extensions.swift */, 923B40C1B730134DE3FB93D56434F359 /* SectionModel.swift */, 2056BF365D2D7CC0FD46ED19EDBEE823 /* SectionModelType.swift */, 3D11C85C7F13C6D98A3A465BC90BAD5C /* Utilities.swift */, 488B1D880370242DA6DAA1BC23EE213D /* Support Files */, ); path = Differentiator; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 17C76A71E1AF9F09E4C42030A2627968 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( F91B75BFF4738F36668933C397A9FCA8 /* _RX.h in Headers */, 3FF87BDA510674F117F6EA54871A33F5 /* _RXDelegateProxy.h in Headers */, 91C5F7C7350D7C7BAF11F0FFB2152C0C /* _RXKVOObserver.h in Headers */, 38F166338687E3EDD813EC86634B915F /* _RXObjCRuntime.h in Headers */, 9DE7A712CF1B72EBDF688D729F09EB2E /* RxCocoa-umbrella.h in Headers */, B8756B693A52F59BA955595B93A8AFE4 /* RxCocoa.h in Headers */, 9D12124A0A7424D36B46A29F495E4394 /* RxCocoaRuntime.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 1C39EE49D85220A1F1F2EBF8D4A95BAE /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 3F911616F29C285D5A4D7F534F4412FC /* Then-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 1D0B0A5525CB3F57F2D64B587D880D88 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( D820DE54D6E9E5C85E0F9BF8A399E5EF /* MJRefresh-umbrella.h in Headers */, 35D685CF7C6F73A65AD3DD99674CA78F /* MJRefresh.h in Headers */, D9F2BA4D713DAC2A85CB4A453240EA3E /* MJRefreshAutoFooter.h in Headers */, D0E7D5B865722A25EC7D91B0FAD8A6CD /* MJRefreshAutoGifFooter.h in Headers */, A5382D3EA98D2EB1AD9EDA5F2138F00D /* MJRefreshAutoNormalFooter.h in Headers */, BA0711638621C2A47AABFF9E03546893 /* MJRefreshAutoStateFooter.h in Headers */, B02D91E02099D3C6A2F988C766032176 /* MJRefreshBackFooter.h in Headers */, 2CEA12D52F3F7AC0669C99A157E19F62 /* MJRefreshBackGifFooter.h in Headers */, 17F033046E658F3BFD0011DCB768CF54 /* MJRefreshBackNormalFooter.h in Headers */, 1ED505CC385A149F4DF8F4B94BA4FC0B /* MJRefreshBackStateFooter.h in Headers */, A72D242BEF61E27D6C29092DF4666A4A /* MJRefreshComponent.h in Headers */, EBEB72F80DCD10DEB82BF5F856D467FD /* MJRefreshConst.h in Headers */, AA4959949A290D4B8BE1E31A890D0C80 /* MJRefreshFooter.h in Headers */, 6A0B1A0FA7A75C861B9D401D0E8CC95E /* MJRefreshGifHeader.h in Headers */, 71F6D96E3AEAA07389F37E2C4B63C4F6 /* MJRefreshHeader.h in Headers */, 7942D242D079A5710A72B659A54D7E26 /* MJRefreshNormalHeader.h in Headers */, BDB33C3708110C6D755175D1F0A2A70B /* MJRefreshStateHeader.h in Headers */, 58E6E68D10EF3D64522BD267CC1761A6 /* NSBundle+MJRefresh.h in Headers */, D02DEBC241C24DEEFDA13C355197A2FB /* UIScrollView+MJExtension.h in Headers */, 0EFB524EFDF2F8712A2F5067817A672E /* UIScrollView+MJRefresh.h in Headers */, DE17E4C2C6EADE5D2D531268B1695405 /* UIView+MJExtension.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 1E0A822016AA6CF232BB4F6126F81008 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 9FEDD419BBF24CFC1354161636496389 /* ReusableKit-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 26DA2FB42087A700B13CACAD4C243527 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 2C33DAACDC03995C3FEC4FA750B506CE /* SnapKit-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 2BF0A2F53314CF5EB4D294D79B51E354 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( A325A1B4D4D686789B291144FB283D47 /* TYCyclePagerTransformLayout.h in Headers */, CA8174FE073791F18FB8C74BB2C4FF57 /* TYCyclePagerView-umbrella.h in Headers */, 40092E84B6E1A2354BEDE901662715E1 /* TYCyclePagerView.h in Headers */, 017B5B39D7D0CCD88DEAB2F1F5D0D274 /* TYPageControl.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 308BF610A5FF6340C2E8EEDF96409AF2 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 1A84FB9D49D5E5E8EC11B4BFA93CD7FE /* Pods-RxXMLY-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 30A3523FB43B8D358EF8AEAD51B489A7 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 273AF2009B201DC6C16AEF4030FFDFE6 /* ReactorKit-umbrella.h in Headers */, D500BAC2F56A14AA3226D7087700D426 /* ReactorKitRuntime.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 36F057C44DEF91262D41374313982B4F /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 6E67DFDCD807369FC97F5F093ADCEB94 /* Differentiator-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 3BF3E42635E5BE1A480B8821B6E82E84 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( D20061C845E39003EB86AB1292B4577D /* TTRangeSlider-umbrella.h in Headers */, A9DCAB1B8D538519A3E04A656155C163 /* TTRangeSlider.h in Headers */, 29560D9109C9140F67F3105A1DE0A5F8 /* TTRangeSliderDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 44C7AC23726DE4E6D35D8A1D17730BA9 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 555AABD226E4FCBD91442DE5AED8483A /* Moya-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 4BF45E5E0A4403672C04711914CE660B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 25B9DC627989966BCF1DCECDABD78D23 /* RxGesture-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 84676F7D46FF521291CDEB2AB04A454C /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 2F288ED3641B2B4775884C462AE377E6 /* Kingfisher-umbrella.h in Headers */, 5B5D8AE167BD8E304CE474A9B8DBA2B4 /* Kingfisher.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 8986C70AFF55B1DD012A7C84159924F9 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 22B40C80735B7CFAC7106C5FF6EA542F /* URLNavigator-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 899E65E945FC87EA2CEC3E51348F11FE /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( E97BD9D3A62BD8225C79AB2D4C4F16B3 /* RxAlamofire-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 8D2725658BE0A873BBAEB9B56FC7946F /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( DDDF354A21835294F7CFB5ACFA5EA968 /* NSObject+Rx-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 9B99F75C95AED0A71535DFF717EBD5DD /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( F3E97A8E1D1ED2B3D24961995923B99A /* SwiftyColor-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; B4002B6E97835FDCCAA5963EFE09A3E0 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 1B9EDEDC964E6B08F78920B4F4B9DB84 /* Alamofire-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; BEB4271AF5F1D6B253A061B69AA6AC34 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 7C8AABC35DC356D57A0766A331DAC4DF /* RxSwift-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; C82B5F04767CB991E84734A54D0ABA5B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 87AD9D97F0F94C85720FC018235CD8A9 /* IQKeyboardManagerSwift-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; F1E7E834BDE614B36F8B992268E4CADD /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( C543308E999E593FE565FCE053658718 /* TYPagerController-umbrella.h in Headers */, 58523F5544BDE0B82DC6E63014A5638F /* TYPagerController.h in Headers */, AF2C3FA58842BEF584ABF0D15CD53311 /* TYPagerView.h in Headers */, 5D448725D385F6D5B24C43CC9448F8E5 /* TYPagerViewLayout.h in Headers */, E0C9EB4ED4DC98789196DD61345ED42A /* TYTabPagerBar.h in Headers */, 87B96B16646AD96D837414B29D2E20C7 /* TYTabPagerBarCell.h in Headers */, C878E06DF50107B7362F7A9FD824E0A9 /* TYTabPagerBarLayout.h in Headers */, 9F189CCBEDAF20A2F80F80B6F37CD18C /* TYTabPagerController.h in Headers */, 176FE089B6CA019DC6F5B0D2521FDFB7 /* TYTabPagerView.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; F4CFBD59DE6B1525E2DAB49E0254EFAC /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 8D5D644E475E21E418B2FBCC9423A471 /* RxDataSources-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; F57C9456EA5FB7C7D8FC40BACD0F5369 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( A128DBCF691E0F4D2AC3EEA36F7BD562 /* ObjectMapper-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; FAE57A9E7839AD2AA87185DB14179BF9 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( A36633C20A13B727FE2F135FB3261A24 /* Result-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; FE18DAC6ED9820DB2FB1CDB05298C88B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 15597F37A6D45F2D0F52932109960222 /* SwiftyJSON-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 0B620384669DABB34C3C5B96EA60F12A /* TYCyclePagerView */ = { isa = PBXNativeTarget; buildConfigurationList = 7CEE893A728FE595E38B19DA5C14A481 /* Build configuration list for PBXNativeTarget "TYCyclePagerView" */; buildPhases = ( 45FC86A2567F803014F88F621D22C796 /* Sources */, AABC11B0DB149687903FAC188E5420A1 /* Frameworks */, 2BF0A2F53314CF5EB4D294D79B51E354 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = TYCyclePagerView; productName = TYCyclePagerView; productReference = 91F4320128E8926FF21C0A92F70CFBBD /* TYCyclePagerView.framework */; productType = "com.apple.product-type.framework"; }; 0F6A8E28D6C2CC2BCA0477C6AF7137F2 /* ReactorKit */ = { isa = PBXNativeTarget; buildConfigurationList = 90DBC69F7407A38678F533AF966E180A /* Build configuration list for PBXNativeTarget "ReactorKit" */; buildPhases = ( 4159A27673E02C6B0AA68F17C1DEC27C /* Sources */, E14E1AD13C72905A9744DCFC4CB6754B /* Frameworks */, 30A3523FB43B8D358EF8AEAD51B489A7 /* Headers */, ); buildRules = ( ); dependencies = ( 74F8F17B6FC071183EB1EC5D2ED0880E /* PBXTargetDependency */, ); name = ReactorKit; productName = ReactorKit; productReference = 195D6043A1C7C9E2F93F03BF5A6C765E /* ReactorKit.framework */; productType = "com.apple.product-type.framework"; }; 1C61647CEE01897368F5D0D2CC181DB4 /* Result */ = { isa = PBXNativeTarget; buildConfigurationList = 554EA923463767BCA4352E1E6C624685 /* Build configuration list for PBXNativeTarget "Result" */; buildPhases = ( 48F5E24EE7C37D644CA2EC2A3A332CFD /* Sources */, 4B7C72EFD055B004BD8D36238863B2D5 /* Frameworks */, FAE57A9E7839AD2AA87185DB14179BF9 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = Result; productName = Result; productReference = BC645F028499DF5A3CB986DD6D4358F1 /* Result.framework */; productType = "com.apple.product-type.framework"; }; 1F5BFD2D8E3672CE19EA16B5208A4066 /* RxCocoa */ = { isa = PBXNativeTarget; buildConfigurationList = 52DD4DD837C8F96EF786BB17F1227792 /* Build configuration list for PBXNativeTarget "RxCocoa" */; buildPhases = ( E29D37D323EFBAB92ED4EB9E402A34FF /* Sources */, 69C6F60DD2457704809BD1D3D4543AD9 /* Frameworks */, 17C76A71E1AF9F09E4C42030A2627968 /* Headers */, ); buildRules = ( ); dependencies = ( 262BA16A4ED2BE76F6D01B3E3A3ECD1B /* PBXTargetDependency */, ); name = RxCocoa; productName = RxCocoa; productReference = E9246ADE8730ABD21F1BBC59333EA31E /* RxCocoa.framework */; productType = "com.apple.product-type.framework"; }; 2166B105EDC9D2499513E6D6BCD586C2 /* RxAlamofire */ = { isa = PBXNativeTarget; buildConfigurationList = 63A8CDD2A9A34DC6C67006A603063404 /* Build configuration list for PBXNativeTarget "RxAlamofire" */; buildPhases = ( 84F38B93AFAA7B8E0B03B786034FA5E9 /* Sources */, 73BF36D607B79591CC85E897840E2802 /* Frameworks */, 899E65E945FC87EA2CEC3E51348F11FE /* Headers */, ); buildRules = ( ); dependencies = ( 91360C912E71D51EA842B318BAEBA511 /* PBXTargetDependency */, C04FA726205A1D05F027F1208191E76E /* PBXTargetDependency */, ); name = RxAlamofire; productName = RxAlamofire; productReference = E02E53CE029ADA867E576C62B9054390 /* RxAlamofire.framework */; productType = "com.apple.product-type.framework"; }; 2D5C20681FC0A1E18576D4673D0EFE65 /* Then */ = { isa = PBXNativeTarget; buildConfigurationList = 55B1DC46A281606C5A8A2F5953589206 /* Build configuration list for PBXNativeTarget "Then" */; buildPhases = ( F68621466263ABC9C173B16DCEA5238C /* Sources */, 674E18361DBF81F73164E63E79DDC140 /* Frameworks */, 1C39EE49D85220A1F1F2EBF8D4A95BAE /* Headers */, ); buildRules = ( ); dependencies = ( ); name = Then; productName = Then; productReference = FB6BED57753CCBC597942DFD41588338 /* Then.framework */; productType = "com.apple.product-type.framework"; }; 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */ = { isa = PBXNativeTarget; buildConfigurationList = 860035FE08E0668CFA26227D241C13AC /* Build configuration list for PBXNativeTarget "RxSwift" */; buildPhases = ( D705382C7BFF2D2C2EC37FEA0F8EEB4B /* Sources */, 23AD706635149AE5F75579590BA4B09C /* Frameworks */, BEB4271AF5F1D6B253A061B69AA6AC34 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = RxSwift; productName = RxSwift; productReference = 0971AB82F462A7C2F67101AD9407905F /* RxSwift.framework */; productType = "com.apple.product-type.framework"; }; 524DC20BC58D4BDA1F0C77763DFA70BE /* ReusableKit */ = { isa = PBXNativeTarget; buildConfigurationList = 0D42361BF39A06A081758B64C4D8FB8D /* Build configuration list for PBXNativeTarget "ReusableKit" */; buildPhases = ( CAEF755B2AF170481A5749470818E9D9 /* Sources */, C9E038B57740EF2F804D34B1C9393F02 /* Frameworks */, 1E0A822016AA6CF232BB4F6126F81008 /* Headers */, ); buildRules = ( ); dependencies = ( A5495B347F8F13816C9E988F7ED6E887 /* PBXTargetDependency */, 49EE20815EBE4FABDDC3320C6B5B116C /* PBXTargetDependency */, ); name = ReusableKit; productName = ReusableKit; productReference = 68668B40B5E550006A59D5621EFB0561 /* ReusableKit.framework */; productType = "com.apple.product-type.framework"; }; 64CC9E274EBD6C16C53FC82EAA6FE9E7 /* NSObject+Rx */ = { isa = PBXNativeTarget; buildConfigurationList = 6252E31AF78FAB4E0F676126FABEADB5 /* Build configuration list for PBXNativeTarget "NSObject+Rx" */; buildPhases = ( 1EA02C2D8C30224ACAEE185D7B4EBBB9 /* Sources */, 1E430C90A89892CE9179EEDDCBFED27E /* Frameworks */, 8D2725658BE0A873BBAEB9B56FC7946F /* Headers */, ); buildRules = ( ); dependencies = ( D957D03022753D4ACA9E09D332D8EEA5 /* PBXTargetDependency */, ); name = "NSObject+Rx"; productName = "NSObject+Rx"; productReference = 84E9E4B67AD852A6167EFE006AD2043F /* NSObject_Rx.framework */; productType = "com.apple.product-type.framework"; }; 6F712943B3C5592E82604940D11CE08A /* Kingfisher */ = { isa = PBXNativeTarget; buildConfigurationList = 8C1E4CEFB2390CBE75E70BAC20F44BBF /* Build configuration list for PBXNativeTarget "Kingfisher" */; buildPhases = ( FD12B015E1FDA3498428EF09C91D1F17 /* Sources */, C0B9722E352CF90C920286E6F029D3C0 /* Frameworks */, 84676F7D46FF521291CDEB2AB04A454C /* Headers */, ); buildRules = ( ); dependencies = ( ); name = Kingfisher; productName = Kingfisher; productReference = 1E1DF457C00960360E254419271FA76F /* Kingfisher.framework */; productType = "com.apple.product-type.framework"; }; 844C9B244CEFA2F26288789C423101B4 /* Differentiator */ = { isa = PBXNativeTarget; buildConfigurationList = 697E3CDF0A55702AF195B90A1000CA13 /* Build configuration list for PBXNativeTarget "Differentiator" */; buildPhases = ( BA8BA23DC65E6F3033B570067BCF4E49 /* Sources */, 2CCEFAA691F3229A3313687F2CC6981E /* Frameworks */, 36F057C44DEF91262D41374313982B4F /* Headers */, ); buildRules = ( ); dependencies = ( ); name = Differentiator; productName = Differentiator; productReference = EFD5B31121E42968628607F4CA11A4BC /* Differentiator.framework */; productType = "com.apple.product-type.framework"; }; 88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */ = { isa = PBXNativeTarget; buildConfigurationList = 419E5D95491847CD79841B971A8A3277 /* Build configuration list for PBXNativeTarget "Alamofire" */; buildPhases = ( 32B9974868188C4803318E36329C87FE /* Sources */, 99195E4207764744AEC07ECCBCD550EB /* Frameworks */, B4002B6E97835FDCCAA5963EFE09A3E0 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = Alamofire; productName = Alamofire; productReference = A6BE7CE8E3CF10B0BE4713CA7CD3FDC9 /* Alamofire.framework */; productType = "com.apple.product-type.framework"; }; 8CB60D59ADD2D72FCB9566114EE33B9A /* Pods-RxXMLY */ = { isa = PBXNativeTarget; buildConfigurationList = 3371F7E9117E31C177556E75F3A0EBB0 /* Build configuration list for PBXNativeTarget "Pods-RxXMLY" */; buildPhases = ( F7E49EE33EA82533063B0336FC00338E /* Sources */, E6817D3D95D3783EE1F540DE3EA42D19 /* Frameworks */, 308BF610A5FF6340C2E8EEDF96409AF2 /* Headers */, ); buildRules = ( ); dependencies = ( 82A7E8FA790BFD2EEAA4CBBFC398A3AD /* PBXTargetDependency */, 7E2C13AE966FB76B2736F2F745A55A84 /* PBXTargetDependency */, 7A1A38F35129D8939DA93941E5566EFE /* PBXTargetDependency */, AE2A28A9913F488DA60A2AE400C7C39C /* PBXTargetDependency */, AFCAAA9C845955F183FAC004DE305241 /* PBXTargetDependency */, FD527F145232A0C224EE6A40F67AB20C /* PBXTargetDependency */, 994AD4F34D30D7AD6C8E508F85E720FC /* PBXTargetDependency */, C0DCC3CCC842C93357AEFDB929B5A252 /* PBXTargetDependency */, C2A6A892DEAC32783AE9D267A1E58534 /* PBXTargetDependency */, 7F53856AB469B6C0D0227D2098D36F1A /* PBXTargetDependency */, 4C8BC7A07FCDEC22BAC47D2EA5B8DB58 /* PBXTargetDependency */, DABB17D04DB056ACFEE47ED47B21D92E /* PBXTargetDependency */, 564A6BC33E46BA3758AE193165BD2546 /* PBXTargetDependency */, B9039DCFEEA782D86F9244953C22F447 /* PBXTargetDependency */, 7DF9738D2EB15BE150084534280EEDBC /* PBXTargetDependency */, A9111896C809CC9110D085304115B040 /* PBXTargetDependency */, 08BBAECE794424C6A743B64C09DB1F15 /* PBXTargetDependency */, 1D6CC51BC907D8D84EC14CFC3A935BF9 /* PBXTargetDependency */, DAB919EDAE0C8E3D7232AFB62120664E /* PBXTargetDependency */, EEF9A4EC5A5005C3AED3CBD2E4BF7DD3 /* PBXTargetDependency */, 10AED09D93D2CD014F7BA195EFC2A01D /* PBXTargetDependency */, 4A1836A5702E2F2BF9536F435BCC4E92 /* PBXTargetDependency */, D56703EF22DD8F7706F17918A2D0AB7F /* PBXTargetDependency */, F5E4336F7DB15BFD3731353A54108BFC /* PBXTargetDependency */, ); name = "Pods-RxXMLY"; productName = "Pods-RxXMLY"; productReference = B2C6C61F18ADC817A64446B3F214A416 /* Pods_RxXMLY.framework */; productType = "com.apple.product-type.framework"; }; 8E38AFA40EB24AAD3998F3F9CFB12CA3 /* SnapKit */ = { isa = PBXNativeTarget; buildConfigurationList = AD3D5B9E24ECB0D75B3E0294644E66D1 /* Build configuration list for PBXNativeTarget "SnapKit" */; buildPhases = ( 36851BD36F2908D027308937D3628C99 /* Sources */, 5642B75CBC2CCDA7E44A7707DD089AF1 /* Frameworks */, 26DA2FB42087A700B13CACAD4C243527 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = SnapKit; productName = SnapKit; productReference = 4F5B061DB45E3912FBD1E11E6F1A842E /* SnapKit.framework */; productType = "com.apple.product-type.framework"; }; 93FD28A3DC61A9094248D0D96CAD9FD4 /* SwiftyColor */ = { isa = PBXNativeTarget; buildConfigurationList = 512FD626A7E3FC83C9CC8A24144BC92B /* Build configuration list for PBXNativeTarget "SwiftyColor" */; buildPhases = ( CFD0F4D99C3A2A82253E9498985556F9 /* Sources */, 2E911FDC90F1E7ACD11F0B68CC7B1FCA /* Frameworks */, 9B99F75C95AED0A71535DFF717EBD5DD /* Headers */, ); buildRules = ( ); dependencies = ( ); name = SwiftyColor; productName = SwiftyColor; productReference = A95B5537CC319932AD399F0660CB16D8 /* SwiftyColor.framework */; productType = "com.apple.product-type.framework"; }; B496A58A7B812A10E0039E15EDF0E8EE /* RxGesture */ = { isa = PBXNativeTarget; buildConfigurationList = C5214E85BC32F855BB77F4FD74672338 /* Build configuration list for PBXNativeTarget "RxGesture" */; buildPhases = ( 6DD8944A25965DD079BE0CA7B844B0C4 /* Sources */, 31195B5F7226702168F8D241576F56D5 /* Frameworks */, 4BF45E5E0A4403672C04711914CE660B /* Headers */, ); buildRules = ( ); dependencies = ( 41F1B2694597D80C8C88DD1E191C2DA0 /* PBXTargetDependency */, 1305CEBDCD0AA3A968FBB90778AC74B5 /* PBXTargetDependency */, ); name = RxGesture; productName = RxGesture; productReference = D5B52B71645C13D27A3C4B3A58EA2440 /* RxGesture.framework */; productType = "com.apple.product-type.framework"; }; C235F77E101E742652CCF98FFDFA5182 /* IQKeyboardManagerSwift */ = { isa = PBXNativeTarget; buildConfigurationList = E8C5CE05A712EF50BB123A41D8EFC0CB /* Build configuration list for PBXNativeTarget "IQKeyboardManagerSwift" */; buildPhases = ( B0CAA73D50E134E589B648ABF6682839 /* Sources */, 81A5C814ACEB86CB06CD6C51383982FE /* Frameworks */, 918D58E08AC7FD6CC66B75FF048005F7 /* Resources */, C82B5F04767CB991E84734A54D0ABA5B /* Headers */, ); buildRules = ( ); dependencies = ( ); name = IQKeyboardManagerSwift; productName = IQKeyboardManagerSwift; productReference = 924F8DE96A22311CC14038BB62C8997D /* IQKeyboardManagerSwift.framework */; productType = "com.apple.product-type.framework"; }; C76585AF9502709B32A11133189DDA23 /* URLNavigator */ = { isa = PBXNativeTarget; buildConfigurationList = 5A05DD5251EAD538258DF17673557953 /* Build configuration list for PBXNativeTarget "URLNavigator" */; buildPhases = ( 5CC50015B125E128985DBB061F47C834 /* Sources */, 0342A957F1A5702D324BDF4037D7D6B3 /* Frameworks */, 8986C70AFF55B1DD012A7C84159924F9 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = URLNavigator; productName = URLNavigator; productReference = 430B138602CD2D328367A37FEE11E65F /* URLNavigator.framework */; productType = "com.apple.product-type.framework"; }; CCEEA2E517C9CB166C75BAF4A263A7CF /* TYPagerController */ = { isa = PBXNativeTarget; buildConfigurationList = 6B1519F70E09D2830A7F592841F05038 /* Build configuration list for PBXNativeTarget "TYPagerController" */; buildPhases = ( F81A606714EAF37F04ADC51B6B6FD751 /* Sources */, 748266AA260D6CE008E3FF2C76A52D04 /* Frameworks */, F1E7E834BDE614B36F8B992268E4CADD /* Headers */, ); buildRules = ( ); dependencies = ( ); name = TYPagerController; productName = TYPagerController; productReference = B228A61D64CA44B4648977930AEEBF5F /* TYPagerController.framework */; productType = "com.apple.product-type.framework"; }; CD6C9FB6B052E1CF663F8A56041EA163 /* ObjectMapper */ = { isa = PBXNativeTarget; buildConfigurationList = 23CFD86DE455F2B998EC09D589DA285D /* Build configuration list for PBXNativeTarget "ObjectMapper" */; buildPhases = ( F35FEC988ECD73463D93176BC71A43B1 /* Sources */, C4F4068871A73EB6FBE8042AF2063595 /* Frameworks */, F57C9456EA5FB7C7D8FC40BACD0F5369 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = ObjectMapper; productName = ObjectMapper; productReference = 68051FD77C0407C9B24515E31E177222 /* ObjectMapper.framework */; productType = "com.apple.product-type.framework"; }; D382A7B90B59D72908CC21054785B020 /* Moya */ = { isa = PBXNativeTarget; buildConfigurationList = 8E2F09332A7598018177CF140EEDFC0E /* Build configuration list for PBXNativeTarget "Moya" */; buildPhases = ( 147471AB17CA6878A911AE963B2EEF4A /* Sources */, 23D3B010AD4A853FE7ED1D97A480065F /* Frameworks */, 44C7AC23726DE4E6D35D8A1D17730BA9 /* Headers */, ); buildRules = ( ); dependencies = ( 2217DC3041FFBBE5E80AAB2A9DC3C930 /* PBXTargetDependency */, 4EB5F10F6AC0963099F3FC814DF14415 /* PBXTargetDependency */, ); name = Moya; productName = Moya; productReference = 3E00F875EA38811C01C6F56B33214C42 /* Moya.framework */; productType = "com.apple.product-type.framework"; }; DE874A99677D06A11E5FCC43C238A7D5 /* SwiftyJSON */ = { isa = PBXNativeTarget; buildConfigurationList = 704D48507B0C8F42CE3629D21F48652A /* Build configuration list for PBXNativeTarget "SwiftyJSON" */; buildPhases = ( 1F428611303CFF0C51F5907C2B17CA73 /* Sources */, 2E345FD99987A0C2F53C3EB5089F2023 /* Frameworks */, FE18DAC6ED9820DB2FB1CDB05298C88B /* Headers */, ); buildRules = ( ); dependencies = ( ); name = SwiftyJSON; productName = SwiftyJSON; productReference = E915131DA668033B6756FDE94DD6C130 /* SwiftyJSON.framework */; productType = "com.apple.product-type.framework"; }; E14A62900910444707A5504D25EC98DC /* MJRefresh */ = { isa = PBXNativeTarget; buildConfigurationList = 1EFADDC2BB6831ED43B76E0241CCDB75 /* Build configuration list for PBXNativeTarget "MJRefresh" */; buildPhases = ( 6D4ECE0A60E69F261AA10FE2CB90A0AC /* Sources */, 1AAD91176E5F5F9AD062AD678F6746E4 /* Frameworks */, 1D0B0A5525CB3F57F2D64B587D880D88 /* Headers */, 8E06EE42A2928B5842D11E8A0A68E87D /* Resources */, ); buildRules = ( ); dependencies = ( ); name = MJRefresh; productName = MJRefresh; productReference = B7B99FDB86B839402402CF4B9085F867 /* MJRefresh.framework */; productType = "com.apple.product-type.framework"; }; E86A9F4C831EEE122A82C7CE19D4011B /* TTRangeSlider */ = { isa = PBXNativeTarget; buildConfigurationList = 0C3A2D1670AC6E68C80F257B426F3A29 /* Build configuration list for PBXNativeTarget "TTRangeSlider" */; buildPhases = ( 69FF2F529D4562CE5A861E242277E2C7 /* Sources */, 7981EDCD0900235B2B3D12DBC172DB34 /* Frameworks */, 3BF3E42635E5BE1A480B8821B6E82E84 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = TTRangeSlider; productName = TTRangeSlider; productReference = 2135AEA738411503ADADD4D2497AA4E6 /* TTRangeSlider.framework */; productType = "com.apple.product-type.framework"; }; F05F33A12963EC1A726FEE15D9000AE2 /* RxDataSources */ = { isa = PBXNativeTarget; buildConfigurationList = BD5CCA9B364C93A19C3C8B5200B7D56A /* Build configuration list for PBXNativeTarget "RxDataSources" */; buildPhases = ( 39A6D6BBB532752CC361197B9D264862 /* Sources */, 943D1D0C588FD46C297C76EB927A6963 /* Frameworks */, F4CFBD59DE6B1525E2DAB49E0254EFAC /* Headers */, ); buildRules = ( ); dependencies = ( 4979E917B04F9F132A2DEC8E47979A28 /* PBXTargetDependency */, 1F022C3FAE5D080B47B2C4EB5A4B7A31 /* PBXTargetDependency */, 5444B96DBCDAD467604CD8796C2A3AE2 /* PBXTargetDependency */, ); name = RxDataSources; productName = RxDataSources; productReference = 97B60E643384171A74CED9D708E98F3D /* RxDataSources.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; LastUpgradeCheck = 0900; }; buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 7DB346D0F39D3F0E887471402A8071AB; productRefGroup = 712E1C09C3CA54FA0B661841A1FB2F97 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */, 844C9B244CEFA2F26288789C423101B4 /* Differentiator */, C235F77E101E742652CCF98FFDFA5182 /* IQKeyboardManagerSwift */, 6F712943B3C5592E82604940D11CE08A /* Kingfisher */, E14A62900910444707A5504D25EC98DC /* MJRefresh */, D382A7B90B59D72908CC21054785B020 /* Moya */, 64CC9E274EBD6C16C53FC82EAA6FE9E7 /* NSObject+Rx */, CD6C9FB6B052E1CF663F8A56041EA163 /* ObjectMapper */, 8CB60D59ADD2D72FCB9566114EE33B9A /* Pods-RxXMLY */, 0F6A8E28D6C2CC2BCA0477C6AF7137F2 /* ReactorKit */, 1C61647CEE01897368F5D0D2CC181DB4 /* Result */, 524DC20BC58D4BDA1F0C77763DFA70BE /* ReusableKit */, 2166B105EDC9D2499513E6D6BCD586C2 /* RxAlamofire */, 1F5BFD2D8E3672CE19EA16B5208A4066 /* RxCocoa */, F05F33A12963EC1A726FEE15D9000AE2 /* RxDataSources */, B496A58A7B812A10E0039E15EDF0E8EE /* RxGesture */, 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */, 8E38AFA40EB24AAD3998F3F9CFB12CA3 /* SnapKit */, 93FD28A3DC61A9094248D0D96CAD9FD4 /* SwiftyColor */, DE874A99677D06A11E5FCC43C238A7D5 /* SwiftyJSON */, 2D5C20681FC0A1E18576D4673D0EFE65 /* Then */, E86A9F4C831EEE122A82C7CE19D4011B /* TTRangeSlider */, 0B620384669DABB34C3C5B96EA60F12A /* TYCyclePagerView */, CCEEA2E517C9CB166C75BAF4A263A7CF /* TYPagerController */, C76585AF9502709B32A11133189DDA23 /* URLNavigator */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8E06EE42A2928B5842D11E8A0A68E87D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 6298B1C2234864DA5ED3B5E6398B95AC /* MJRefresh.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 918D58E08AC7FD6CC66B75FF048005F7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 2A2FE216328BFC23AF9D30EAEFAA2303 /* IQKeyboardManager.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 147471AB17CA6878A911AE963B2EEF4A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 839A55AD3279D154DC55C93BE714B93E /* AccessTokenPlugin.swift in Sources */, 5BB8EA4A2E5BEAB53FD3C34496F3CEDB /* AnyEncodable.swift in Sources */, 6BAE5A1DC9D8A6DED87FF11DEA68C04D /* Cancellable.swift in Sources */, D947452217EE9CF7F39D5F25C226DACC /* CredentialsPlugin.swift in Sources */, 068EE77B364B309A5973AA63DCE763D0 /* Endpoint.swift in Sources */, 7B0FACE97300F45175045FD090F70CE2 /* Image.swift in Sources */, B3D2B497C3F6721D93B9D8C6E459B763 /* Moya+Alamofire.swift in Sources */, B9AB0B01A8F7F6D3C83AEE614C9304C2 /* Moya-dummy.m in Sources */, 2641C5F2AF734454E8A6DF94E606EE71 /* MoyaError.swift in Sources */, A4A7E0E7713372CEF2409C4B6E564837 /* MoyaProvider+Defaults.swift in Sources */, A03ADFA996990D2DA836E6FFCEE37F36 /* MoyaProvider+Internal.swift in Sources */, ED4CDFA145F591A93A57DC1B7CE1C12E /* MoyaProvider.swift in Sources */, 3433BF7613AB168C91A7310C0B5CE6A9 /* MultipartFormData.swift in Sources */, 4A54582B320910433D1A82740A41FAF1 /* MultiTarget.swift in Sources */, 03905D0FD58E772457B809C9443B9633 /* NetworkActivityPlugin.swift in Sources */, 36C33EC0952D217038E06134251A4267 /* NetworkLoggerPlugin.swift in Sources */, EC0418D76D4ADA0C663EE5E8D2D9B57C /* Plugin.swift in Sources */, 2968F27F99F394EABFF42645167C47A7 /* Response.swift in Sources */, A9CA258A1B6D95AA156B0601E9473EF5 /* TargetType.swift in Sources */, CEA192A80E93599EFC5BEC6EE1F97BB5 /* Task.swift in Sources */, A46ECAD1FE903ECD9E035F9DB92CCEFE /* URL+Moya.swift in Sources */, 79B3581182D487C5DBEF24D7E8B263BE /* URLRequest+Encoding.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 1EA02C2D8C30224ACAEE185D7B4EBBB9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 29188F71B40A3F6A817C5369E3DBA7FF /* HasDisposeBag.swift in Sources */, 5BAEAF4DF2226C38891357D17E315D91 /* NSObject+Rx-dummy.m in Sources */, 6F5E2DD4FF386EBB8EB55967964C5BE3 /* NSObject+Rx.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 1F428611303CFF0C51F5907C2B17CA73 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( EF3D53302381F94241804E5CD95AA64F /* SwiftyJSON-dummy.m in Sources */, 03CE3310204ECD17C80FA496A818DA47 /* SwiftyJSON.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 32B9974868188C4803318E36329C87FE /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 9ED2BB2981896E0A39EFA365503F58CE /* AFError.swift in Sources */, A9EEEA7477981DEEBC72432DE9990A4B /* Alamofire-dummy.m in Sources */, F8B3D3092ED0417E8CDF32033F6122F5 /* Alamofire.swift in Sources */, 61200D01A1855D7920CEF835C8BE00B0 /* DispatchQueue+Alamofire.swift in Sources */, B65FCF589DA398C3EFE0128064E510EC /* MultipartFormData.swift in Sources */, A2A6F71B727312BD45CC7A4AAD7B0AB7 /* NetworkReachabilityManager.swift in Sources */, EFD264FC408EBF3BA2528E70B08DDD94 /* Notifications.swift in Sources */, BE5C67A07E289FE1F9BE27335B159997 /* ParameterEncoding.swift in Sources */, 5387216E723A3C68E851CA15573CDD71 /* Request.swift in Sources */, CB6D60925223897FFA2662667DF83E8A /* Response.swift in Sources */, F6BECD98B97CBFEBE2C96F0E9E72A6C0 /* ResponseSerialization.swift in Sources */, 7D8CC01E8C9EFFF9F4D65406CDE0AB66 /* Result.swift in Sources */, 62F65AD8DC4F0F9610F4B8B4738EC094 /* ServerTrustPolicy.swift in Sources */, 7B5FE28C7EA4122B0598738E54DBEBD8 /* SessionDelegate.swift in Sources */, AE1EF48399533730D0066E04B22CA2D6 /* SessionManager.swift in Sources */, 3626B94094672CB1C9DEA32B9F9502E1 /* TaskDelegate.swift in Sources */, 10EB23E9ECC4B33E16933BB1EA560B6A /* Timeline.swift in Sources */, BBEFE2F9CEB73DC7BD97FFA66A0D9D4F /* Validation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 36851BD36F2908D027308937D3628C99 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0EEBFA3280A544CB4F48B94672825C03 /* Constraint.swift in Sources */, 0DD634DCA2EE6C8DA4C13EEC73849EFD /* ConstraintAttributes.swift in Sources */, B83AC06EC6750F396E898A09BE4BC42B /* ConstraintConfig.swift in Sources */, 8D6FC3FD5C6F9AB903E885369D8C437F /* ConstraintConstantTarget.swift in Sources */, 157E33FBE3B7D1D7996ADD2848628BC2 /* ConstraintDescription.swift in Sources */, 507559646F241781DF5F959957D54C98 /* ConstraintDSL.swift in Sources */, 95DC0825394C10B2BCA74208761E16E6 /* ConstraintInsets.swift in Sources */, 65CDABE73636295123CD8259D16ACFC1 /* ConstraintInsetTarget.swift in Sources */, 1F34D539C0D864807B558FC80839C15A /* ConstraintItem.swift in Sources */, 866B48826FBBC43E96593CF27485FFC1 /* ConstraintLayoutGuide+Extensions.swift in Sources */, 8BF6B3A9B39B75AA591626B7B26175DE /* ConstraintLayoutGuide.swift in Sources */, BCEF70E63A638C19CEE89A53D7FFE523 /* ConstraintLayoutGuideDSL.swift in Sources */, 79F5CE53E58BED49036519F4E1197395 /* ConstraintLayoutSupport.swift in Sources */, 66407236B5F3BF75ECBAE79FB585EC78 /* ConstraintLayoutSupportDSL.swift in Sources */, 4F486A2A1239E02C62E27B6DB5EBF025 /* ConstraintMaker.swift in Sources */, F4988BB5862C381854506C82E43136A3 /* ConstraintMakerEditable.swift in Sources */, E9E28E9BEA02135CFBF9E598CC1885D2 /* ConstraintMakerExtendable.swift in Sources */, 9A8BECF84F3F50D260FB7132B311E932 /* ConstraintMakerFinalizable.swift in Sources */, ABB86D5B62EC96B6997FBE8361498297 /* ConstraintMakerPriortizable.swift in Sources */, 9980108FD8F8C2F7FF71BD9DCAEAD6DF /* ConstraintMakerRelatable.swift in Sources */, 0DC7585DE4582FFC698D5CA7F7987868 /* ConstraintMultiplierTarget.swift in Sources */, CEDBD76A18EEEDCA4EBAC563A97D2C51 /* ConstraintOffsetTarget.swift in Sources */, 54B5B7D9E6EFC9C2C7B051BF214B9069 /* ConstraintPriority.swift in Sources */, 671B54E4AAC34ADFF8D1C372330E98FB /* ConstraintPriorityTarget.swift in Sources */, BAFB96EEF09DA050757292A2F5EA6404 /* ConstraintRelatableTarget.swift in Sources */, 15A0DC01842AFC68403331D8324AC790 /* ConstraintRelation.swift in Sources */, 3185243C5F5AD72D5DF9D3D7CEAD5237 /* ConstraintView+Extensions.swift in Sources */, C3CFEB87CAB11F928DAD084B87C7F26C /* ConstraintView.swift in Sources */, B46EEAED7469BDA363E075F37016248A /* ConstraintViewDSL.swift in Sources */, 4257A939388AC78D0B3466520A06879B /* Debugging.swift in Sources */, 32995C9F464270A5E2D36056E9B5190B /* LayoutConstraint.swift in Sources */, A006A7EEF7652FE9322878C39A36CED3 /* LayoutConstraintItem.swift in Sources */, 1500C42EA85F1F32A0DFA87FE8320E24 /* SnapKit-dummy.m in Sources */, 8C8833BAF8EEDBF0491FC6CFEAF001E0 /* Typealiases.swift in Sources */, 538546EBA321B92342C272F36B39C23B /* UILayoutSupport+Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 39A6D6BBB532752CC361197B9D264862 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 2615F7FE9AF4BF4955E9CB747E98FC21 /* AnimationConfiguration.swift in Sources */, EA326BB6A8ACA0EE3C0B44849198ADE6 /* Array+Extensions.swift in Sources */, 71BA33FC95D59E6C0FA6DEEF747351B7 /* CollectionViewSectionedDataSource.swift in Sources */, A5FF3C7A501FACB556E67C86C1990F7D /* DataSources.swift in Sources */, 51390FB969CB66F4D75C47503E00A1D2 /* Deprecated.swift in Sources */, 647C9A69799E80C9EFB477D0ED6D2406 /* FloatingPointType+IdentifiableType.swift in Sources */, E36C37C4AC1FFD475C4490193C66FF22 /* IntegerType+IdentifiableType.swift in Sources */, 96EED7813D6F11E4A799B9B76F391500 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */, 283410C5EE7D346795416AC105C37733 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */, 674EB63D9FF75C7AB07BA9DA46D8AE09 /* RxDataSources-dummy.m in Sources */, 2F711ADC9F8930796D22855B9DD7AE42 /* RxPickerViewAdapter.swift in Sources */, 4AD94A043ECE1C2D0B98F467E75D40C2 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */, 476CAF0EE5F9011302EF906E33A10687 /* RxTableViewSectionedReloadDataSource.swift in Sources */, CDB48F0785D88989F4C7EB6C419AB6B0 /* String+IdentifiableType.swift in Sources */, E027FBCDAF65778317E634F4A92512B9 /* TableViewSectionedDataSource.swift in Sources */, 891A61A41441BA89E0055C960A4664ED /* UI+SectionedViewType.swift in Sources */, C100B07C3830CD742E8C9D9E427B1E3B /* ViewTransition.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 4159A27673E02C6B0AA68F17C1DEC27C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F39A3624015D4234E6FE35761F53BC93 /* ActionSubject.swift in Sources */, 3AF8D0984A8BD088E7135F79FCA0AD78 /* AssociatedObjectStore.swift in Sources */, AC09A4779981FD4CD0F95DC2054E4C99 /* Reactor.swift in Sources */, 6DB8DA4031D9691E8E809A385F4A5846 /* ReactorKit-dummy.m in Sources */, DDDB37E35D9BC501950FB4909B609F2E /* ReactorKitRuntime.m in Sources */, FE782FADB39D1A5F08B981F6F7D27726 /* StoryboardView.swift in Sources */, C3A7EF039070A3D3EE385B130DB94EEB /* Stub.swift in Sources */, B948B6BD2A647A1DCC1401170B5D09FA /* View.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 45FC86A2567F803014F88F621D22C796 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 82443926248B0468C2318B68ABB2FE14 /* TYCyclePagerTransformLayout.m in Sources */, CED1FC24A094EABAF628E86E2BCBB656 /* TYCyclePagerView-dummy.m in Sources */, F8DB73FCAA437CAC5D12AFBA1DD3D205 /* TYCyclePagerView.m in Sources */, 69D61F970DB381B6094C0E8085CB1E00 /* TYPageControl.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 48F5E24EE7C37D644CA2EC2A3A332CFD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 795ED1A351690C7143540C8CB7AA6CD0 /* Result-dummy.m in Sources */, EFCDB86F2D87E50B9B3EAA6827FF4012 /* Result.swift in Sources */, 71DD71770F915F1621F0155CFD6CCEDD /* ResultProtocol.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 5CC50015B125E128985DBB061F47C834 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DF1BF64A08EBF0DE6CD67ED0D49CB07B /* Navigator.swift in Sources */, 8B5BA7E0D60BA533FA43EF493F4D5D83 /* NavigatorDelegate.swift in Sources */, AA52CCA36AE2BBAA57E7DB6D3B437F6F /* NavigatorType.swift in Sources */, C2210016F369741901DB640E70B60688 /* UIViewController+TopMostViewController.swift in Sources */, 98F5BB1FF23CDD9DDAE74AE7DF766672 /* UIViewControllerType.swift in Sources */, 0A9C9898D2B1246BFC5F26FC2E44F312 /* URLConvertible.swift in Sources */, E898E416B77BB93F9D6E37227C723D81 /* URLMatcher.swift in Sources */, 955203B31E419BFF9E9DC5EC02787D6B /* URLMatchResult.swift in Sources */, 3666B742397E794ED820CC7AF304560C /* URLNavigator-dummy.m in Sources */, BCF96CF7173DBD9B80B2DA5EDB3A9119 /* URLPatchComponentMatchResult.swift in Sources */, B81E85BB5B30F34AAED56494D329A74D /* URLPathComponent.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 69FF2F529D4562CE5A861E242277E2C7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( A6FD129851497CE8AB256289C1BD6463 /* TTRangeSlider-dummy.m in Sources */, FD1D0607A2C283A73BF4568A7076EA30 /* TTRangeSlider.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6D4ECE0A60E69F261AA10FE2CB90A0AC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 621B413DAEFE75991251ED378822527C /* MJRefresh-dummy.m in Sources */, D46545D206874E3A223372C84F1EA73E /* MJRefreshAutoFooter.m in Sources */, 7EFDAA1642FA83DE777CFD086182D5A4 /* MJRefreshAutoGifFooter.m in Sources */, 344DB99F8332029C0391F08B6D0E425C /* MJRefreshAutoNormalFooter.m in Sources */, 1843E6DF520A5EFE9C83C38E18578E42 /* MJRefreshAutoStateFooter.m in Sources */, E80DB5D9B2D35085282DF057BB540AFE /* MJRefreshBackFooter.m in Sources */, D66E78D63935A98894E702D922AA8B91 /* MJRefreshBackGifFooter.m in Sources */, 101F836F887C01B45F4DCF351EBC28D1 /* MJRefreshBackNormalFooter.m in Sources */, 2524EC4EF024A6C067A0FA082084CFB5 /* MJRefreshBackStateFooter.m in Sources */, F68EA720B0ED1BF96CEBF07977A1C0A2 /* MJRefreshComponent.m in Sources */, 8069223DDD6D0F2E1A87D8E66966709B /* MJRefreshConst.m in Sources */, 3829CBCC3598DB13C2AD3B7383D1F24A /* MJRefreshFooter.m in Sources */, FD31283E2A43955C5BA6FA9008EE8D7C /* MJRefreshGifHeader.m in Sources */, 8DC3FC2342EC2E944B3A3E83E2683130 /* MJRefreshHeader.m in Sources */, E57312010AFB591EE37870F65E651EA6 /* MJRefreshNormalHeader.m in Sources */, 07159A933B3C47A02FE2B2744ABAAC89 /* MJRefreshStateHeader.m in Sources */, F4A57F7C1F8184A6DDEABB07FA6D7083 /* NSBundle+MJRefresh.m in Sources */, FB9C6242B11049E2F1062D04565E568D /* UIScrollView+MJExtension.m in Sources */, 8B855A73B75155FB2C0CB05B54FDDE0D /* UIScrollView+MJRefresh.m in Sources */, 0A1E34B4A27DCD62A914F3B028A07E6C /* UIView+MJExtension.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6DD8944A25965DD079BE0CA7B844B0C4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 306C95EBAA55143914CC0AAA88E769D1 /* GestureFactory.swift in Sources */, 3B1C65D9BB266645228E3FF013A67A62 /* RxGesture-dummy.m in Sources */, FA632862F1071D6B51FC158FA50367D8 /* RxGestureRecognizerDelegate.swift in Sources */, D6139FB200F1DB52CB02F86050D2C555 /* SharedTypes.swift in Sources */, 4CF6C6317C8C75CCE3E05761E0A923AC /* TransformGestureRecognizers.swift in Sources */, 8CD28DDD291AB1B9D0F6DE817CE995FE /* UIGestureRecognizer+RxGesture.swift in Sources */, A28F98C3C629B57C326A9FE9A51CE133 /* UILongPressGestureRecognizer+RxGesture.swift in Sources */, EF12BF89DE7448AEE5CE6F0F8DCA3F9A /* UIPanGestureRecognizer+RxGesture.swift in Sources */, DD311E4BCAE7DD4AA5C938968D72AAAF /* UIPinchGestureRecognizer+RxGesture.swift in Sources */, F43A145ECD909D880FDC70E638AB8C13 /* UIRotationGestureRecognizer+RxGesture.swift in Sources */, D5CAE5F9ED6C5FFE2911D544A442B8DF /* UIScreenEdgePanGestureRecognizer+RxGesture.swift in Sources */, 8EC3A1FE52FACEFF6171B96607440D7C /* UISwipeGestureRecognizer+RxGesture.swift in Sources */, 2950762546DE22AB539CDBD87B000951 /* UITapGestureRecognizer+RxGesture.swift in Sources */, EF412419B6C1CD59B3BBC3836BD43767 /* View+RxGesture.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 84F38B93AFAA7B8E0B03B786034FA5E9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 8FBA14759D5686E201066D8DD81D2794 /* RxAlamofire-dummy.m in Sources */, CDAD14D7F2DC3BF192411C56B374C172 /* RxAlamofire.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; B0CAA73D50E134E589B648ABF6682839 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F48DD4AF7C254B7A2F67A690AF8EF143 /* IQBarButtonItem.swift in Sources */, 4882783C5576C5D43CE79C03DE33C3DD /* IQKeyboardManager.swift in Sources */, 9B9C1A095D68E3B91BD38F910C6F2E7F /* IQKeyboardManagerConstants.swift in Sources */, EC2CCB07B8CCEF669FAF345D29691030 /* IQKeyboardManagerConstantsInternal.swift in Sources */, B0EF72D8E2CB6594FEE4DB2600B32650 /* IQKeyboardManagerSwift-dummy.m in Sources */, C40F6D2FE20755C045864C23F73260C8 /* IQKeyboardReturnKeyHandler.swift in Sources */, CC93854C66A808596F5A3B58FDB49BF8 /* IQNSArray+Sort.swift in Sources */, CB29E86F679E9DDC5C5A1C89482701CA /* IQPreviousNextView.swift in Sources */, 88A25F661A4435D57041D21700515CC6 /* IQTextView.swift in Sources */, 1F0F49D029C4471F2AE3712D1FCB1D8A /* IQTitleBarButtonItem.swift in Sources */, 02A8CD406255AE081E99A62241BAC587 /* IQToolbar.swift in Sources */, 2384AC373E752D55842F37DE4F2CE2A7 /* IQUIScrollView+Additions.swift in Sources */, B1A7ADAE2CAEDA5D703CE5C6A6DA405B /* IQUITextFieldView+Additions.swift in Sources */, 3077B4186B916A8DB980BBB72DF7FFB9 /* IQUIView+Hierarchy.swift in Sources */, 9C178A996A6A0F14CF4B1F1D1BA4B2EB /* IQUIView+IQKeyboardToolbar.swift in Sources */, 866952184CCB9236E8D567A97A7C3224 /* IQUIViewController+Additions.swift in Sources */, 64FFC1F647245F61C405AE7AC1DA9D31 /* IQUIWindow+Hierarchy.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BA8BA23DC65E6F3033B570067BCF4E49 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 7718718BA955BC1ADF13A14CF2FAEC0D /* AnimatableSectionModel.swift in Sources */, AF5F7A48DB63AB10243894FA9BA673A0 /* AnimatableSectionModelType+ItemPath.swift in Sources */, FD99B1C2C751D10EFC95B148A9028316 /* AnimatableSectionModelType.swift in Sources */, 51B625C8BDBF7B247899FF64794370B8 /* Changeset.swift in Sources */, 609E700D79249C49A9EF7D6F72440F34 /* Diff.swift in Sources */, 8253C04716B2C6BE88755512C4BB2D27 /* Differentiator-dummy.m in Sources */, DE8B3FC90EA6A88A4B44039572481C86 /* IdentifiableType.swift in Sources */, F64167E53EFA6CC3537752E4C2442990 /* IdentifiableValue.swift in Sources */, F363D1083A5EFBE7CE56067520330A9D /* ItemPath.swift in Sources */, F4ECE99FE56932FCA2EB2B238C8EA43E /* Optional+Extensions.swift in Sources */, EA4F7446E804B5FD266D16B9E9AE5374 /* SectionModel.swift in Sources */, 6FA0019746BB30AF58E427FD0CBCEE3C /* SectionModelType.swift in Sources */, D371307D7540D73B2DC187E81186B097 /* Utilities.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; CAEF755B2AF170481A5749470818E9D9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D0121BBC81F4FA5DEF3D0A4F405BC605 /* ReusableKit-dummy.m in Sources */, 78393A7387E5886A01E30F08172D3963 /* ReusableKit.swift in Sources */, 6CC98E6987E27D6CCDDFAE2F012A013C /* UICollectionView+ReusableKit.swift in Sources */, A345A8C3AD50AA10257D9C7F63F7D5EA /* UICollectionView+RxReusableKit.swift in Sources */, 97D88C924A7F6C1481FAECCC3E5E56E0 /* UITableView+ReusableKit.swift in Sources */, 48700A675D816D1D4D07E26087D0A5C8 /* UITableView+RxReusableKit.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; CFD0F4D99C3A2A82253E9498985556F9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C1A095D5DB6C70109EE936A914E53709 /* SwiftyColor-dummy.m in Sources */, AF0CD2BD2A2074BF1A98000D204275A5 /* SwiftyColor.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; D705382C7BFF2D2C2EC37FEA0F8EEB4B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( EA811EC31868E728B8BE88F94CA53B19 /* AddRef.swift in Sources */, A2E8853B9D79DAA9B2512F7AF251C587 /* Amb.swift in Sources */, 10B28E1329EC509470D6073426296383 /* AnonymousDisposable.swift in Sources */, E2F35C82BD4AB36C18F735165A7683EE /* AnonymousObserver.swift in Sources */, 8757DAF954F1D3C28C865BCFE62341D1 /* AnyObserver.swift in Sources */, 022580D7BA3860D083FC817324DDD52D /* AsMaybe.swift in Sources */, F931EEDE05DA8151D729C5C39F5CBE83 /* AsSingle.swift in Sources */, 4323B40B40C5D9EF55A1E88569EE9C8E /* AsyncLock.swift in Sources */, 9ACC05833323E3CB2FD5DEE095CFBD71 /* AsyncSubject.swift in Sources */, AEF1257C803A37C026D774C6EB1F5A8B /* Bag+Rx.swift in Sources */, A6883C52C0D62642960508D2E64CB789 /* Bag.swift in Sources */, 96599CA3482E11E508F3607F678A4075 /* BehaviorSubject.swift in Sources */, 70F84C166016A96F0493CAE9BAA238F2 /* BinaryDisposable.swift in Sources */, CF3B98E3589779E82B30019BF13739D4 /* BooleanDisposable.swift in Sources */, 340B17F6811B1F4D98C92209B4A68C5A /* Buffer.swift in Sources */, BD443E89A222B5234FE7167CC470030A /* Cancelable.swift in Sources */, D989E0CB9CB56EBC807116DB668019AE /* Catch.swift in Sources */, 95C52B5A296DEDDA23804E0EB3441E98 /* CombineLatest+arity.swift in Sources */, B467718840D63E04D0D3AD556E11E665 /* CombineLatest+Collection.swift in Sources */, C1CF0A6316B4563F05B117FDABF76AD8 /* CombineLatest.swift in Sources */, 101F0992A8ADA071018179C8A35A2DB6 /* Completable+AndThen.swift in Sources */, 023B9DEF425CEF2B0993CC116B9D7748 /* Completable.swift in Sources */, 6508C244210F686EA369416EA9FA88E9 /* CompositeDisposable.swift in Sources */, 8EAB5DCA47437AE6A858FC683FBA23C6 /* Concat.swift in Sources */, CC3F2D5828482FEE2984ACA3FDF23349 /* ConcurrentDispatchQueueScheduler.swift in Sources */, 52D0B3F0EA5BCADED39162E5560909F9 /* ConcurrentMainScheduler.swift in Sources */, EE855D5B4F46A1DB974602210A435D93 /* ConnectableObservableType.swift in Sources */, DA7EB935B3E95B385300ECE8D38F71B6 /* Create.swift in Sources */, 2A1B0B5AAC6B121823473D167C5B372E /* CurrentThreadScheduler.swift in Sources */, 2D628BD8A654993F779B1C05DC8A4F4C /* Debounce.swift in Sources */, F1733F37EB2E6EF05781BB4DA571B204 /* Debug.swift in Sources */, 59AA965004671689A9B41955CB6BA511 /* DefaultIfEmpty.swift in Sources */, A788C5E58CFBB81C97345BAA4C6A292B /* Deferred.swift in Sources */, 3AF4EE85A4140EBA123BA54FBFC1ADF9 /* Delay.swift in Sources */, 09FA411E4CCB0B52628890F610065A66 /* DelaySubscription.swift in Sources */, 1C8AFF6985D76BB339539D41CCC403F6 /* Dematerialize.swift in Sources */, BABE350B5A22F812D599C5C016EAA1C4 /* Deprecated.swift in Sources */, 186AC9E5D41A13F254AA6A111A659C86 /* DispatchQueue+Extensions.swift in Sources */, 208482EE9B0AF0B0557F8E32F1B7A290 /* DispatchQueueConfiguration.swift in Sources */, A580F7392F3E5BD4B04688E9FE75B88F /* Disposable.swift in Sources */, 4EA164050B9B1B1344A8984EC924D34E /* Disposables.swift in Sources */, C273C74217117E95DFB32C153F6DAA84 /* DisposeBag.swift in Sources */, 9A726AE6822AF4B3AE67808BFB9845F9 /* DisposeBase.swift in Sources */, F95FC708ACF2B7648DFBCD974ACD9B89 /* DistinctUntilChanged.swift in Sources */, F07FA872B5DE108E5F0B486E7F183E77 /* Do.swift in Sources */, E90FA56D8781CEEECA7E5AD108103434 /* ElementAt.swift in Sources */, DDAD3A47F10FBBB697E079CAB38EC31C /* Empty.swift in Sources */, 5AE90602C51230C8DA03B35061E42AA4 /* Enumerated.swift in Sources */, F71C6DC478982AEAFDE2B7040BC82D67 /* Error.swift in Sources */, 18C27FC3DA935FF02249B00D2410C6D3 /* Errors.swift in Sources */, DBF869BBD4D1B55C2D5CFE5EAE756F9F /* Event.swift in Sources */, 4AFA156443C8EF0EDABD03E6626DAB5F /* Filter.swift in Sources */, 4CECE91A2400A1389FB240DA09F92D44 /* First.swift in Sources */, 3D68B2DD0D49F92CFE4D07447D4861FA /* Generate.swift in Sources */, FFE963243CB86C7F5CC3F386683393FA /* GroupBy.swift in Sources */, 41F2693C7BDA9AA4141C2017F724EB92 /* GroupedObservable.swift in Sources */, 988CE455361E53CE47125E2E64F7279C /* HistoricalScheduler.swift in Sources */, F34B7F0D30A6D72E9FBF2242712BDF9D /* HistoricalSchedulerTimeConverter.swift in Sources */, E560CBBCB2A8426232A0C237818CB350 /* ImmediateSchedulerType.swift in Sources */, 0638F162B79029D25571C968F00A8721 /* InfiniteSequence.swift in Sources */, E4C77EA0646C587F18D7AEC063AA13AA /* InvocableScheduledItem.swift in Sources */, 0EEB4473FC528447BEC20C32448BF58E /* InvocableType.swift in Sources */, 068C7761D808018F145A97CA20A231BC /* Just.swift in Sources */, 6D5E66010A47E24AC918B48051FA9B3B /* Lock.swift in Sources */, 1E340BDD912879B4160C43D9982104EC /* LockOwnerType.swift in Sources */, 2E1C908C7B515E06E6ACE5D56B2D0037 /* MainScheduler.swift in Sources */, FD11E5E7BE23D70131359C06A7C63B94 /* Map.swift in Sources */, 4639BFED317F19A1BA465B22250228A1 /* Materialize.swift in Sources */, 084482D74A1740BF8258EEF9FEC9C93A /* Maybe.swift in Sources */, 0FD6CD7FBCB5EB6CEC1CABC3CAE78B12 /* Merge.swift in Sources */, C9C7FF8901714AEEF66A96DEC28B4C51 /* Multicast.swift in Sources */, 5160BA1430D2179A43B3A0B558F23E1F /* Never.swift in Sources */, 9732F130B48FC5DDA707612D58A7BEC9 /* NopDisposable.swift in Sources */, D9AAAFC710311CB167E506163E994A4D /* Observable.swift in Sources */, 5AC44C9876A3D343C63CDE76A4139036 /* ObservableConvertibleType.swift in Sources */, E402F8791AB3E6BE18F05C1265668896 /* ObservableType+Extensions.swift in Sources */, 5F59C423FBE9B7D79A9CDB48BCE24B3E /* ObservableType+PrimitiveSequence.swift in Sources */, 8BB9815D2C57EA5200DCE170988860DB /* ObservableType.swift in Sources */, 72F8287CD5471AE0376C28EC9AD903CA /* ObserveOn.swift in Sources */, 96E58EA8ACCB671460FB4D4A98113D92 /* ObserverBase.swift in Sources */, 7FEE8F5AEE7AF4879B8911612CF82CCC /* ObserverType.swift in Sources */, 5D97452371AFE3FBC01EF1B84633C34B /* OperationQueueScheduler.swift in Sources */, B2B98FD2647A3FA41300E78136335B3B /* Optional.swift in Sources */, 3F7E121270CEB5622C8E10DE12E11E40 /* Platform.Darwin.swift in Sources */, 442CB576089010AAA46D4D5977388D87 /* Platform.Linux.swift in Sources */, 5288D9F32C3135D4FEF0A9601C153E7F /* PrimitiveSequence+Zip+arity.swift in Sources */, CB3BEC3D885EFDE2FE24B5A4F6FC16E7 /* PrimitiveSequence.swift in Sources */, 47CA831BE2229583488AE4A12E320BCD /* PriorityQueue.swift in Sources */, 1784A1AE7CB88EA9BF1A9CF7377C8090 /* Producer.swift in Sources */, 6303CB98027B59C02EA4EFB216E7069B /* PublishSubject.swift in Sources */, 42C4C9573928B869632A7829D9A6BCBE /* Queue.swift in Sources */, 289F681251A15B7EF356F7198CB3A5CE /* Range.swift in Sources */, 6129D5C1F81A4991CEFA556838395C58 /* Reactive.swift in Sources */, 6E116732E3E785C9F5D69045E6371E71 /* RecursiveLock.swift in Sources */, 8A1AF61FB826C9D132ABFC0195471179 /* RecursiveScheduler.swift in Sources */, B6E2FEDDD91D886E3BA3F1F9D338E672 /* Reduce.swift in Sources */, 051F11606D79C85C63FD6BDEE5C2A6E8 /* RefCountDisposable.swift in Sources */, 4CE7FCD6E10E191F7811CFB81CFDAF6C /* Repeat.swift in Sources */, 8B6ADD6C31B20E3FC60C00F494B59112 /* ReplaySubject.swift in Sources */, F793B5FB9A2D4726AF9CB4E564BFE650 /* RetryWhen.swift in Sources */, 2616DFB5A134CFB3D2F88848CA6730A6 /* Rx.swift in Sources */, B33491A4893CF9905D4A8DF6A8BB52A1 /* RxMutableBox.swift in Sources */, 2478D027E97BFE0F71CF21D8B9E8020F /* RxSwift-dummy.m in Sources */, 4303F3EF9822CB9AEB720051B0FD4F6B /* Sample.swift in Sources */, AA5D493C14AE53D874A31A847787D800 /* Scan.swift in Sources */, 4891FF4C052E475B1712F1251F9E3D9D /* ScheduledDisposable.swift in Sources */, AC8523DFDA128333C4B596BEE5DDD3BD /* ScheduledItem.swift in Sources */, 60F43A8950189140E8E60155EA3F403F /* ScheduledItemType.swift in Sources */, 5E30EEC06CCBCF67205D284B1FC7496F /* SchedulerServices+Emulation.swift in Sources */, 216DC034D3FF185097D5C162323EA37C /* SchedulerType.swift in Sources */, CFB93AB1B5504D35D1E6664972D62F19 /* Sequence.swift in Sources */, 770E2F9E0A86F1290A34AD9C5FBA2128 /* SerialDispatchQueueScheduler.swift in Sources */, C5C6E5A84362150667FF88B6C18C0DAB /* SerialDisposable.swift in Sources */, 3244910D451D0BBF516D27D067C9A5C4 /* ShareReplayScope.swift in Sources */, 74EBBCE7876A5AE3C73D7386EB85D4A4 /* Single.swift in Sources */, ADE879A06835C91322843C21A58C5C40 /* SingleAssignmentDisposable.swift in Sources */, C059355C2838DB2D4A2796B41543511A /* SingleAsync.swift in Sources */, 9EE9EF9B3B2B22A38445D287017EFC0B /* Sink.swift in Sources */, 49FC00F712C285D51C1C0708521BB01C /* Skip.swift in Sources */, 2396E8B97AED713A2F93792BB324738B /* SkipUntil.swift in Sources */, 07786426A59FC42A5077B8B28EF25834 /* SkipWhile.swift in Sources */, 75D28DC57BA76546CEDC5EFA5453E974 /* StartWith.swift in Sources */, 11F2729CD19FF2CBFE91E3D4A5A3D088 /* String+Rx.swift in Sources */, 579D9C74D7293D142E36B5DB1CF0E320 /* SubjectType.swift in Sources */, 1D16749675E7E62321F1D550ED6CE78F /* SubscribeOn.swift in Sources */, EB2866D890A7B0D65C8AB7F37614C15F /* SubscriptionDisposable.swift in Sources */, 13575C4CA4D1D8F0DBB6E2164CBFF620 /* SwiftSupport.swift in Sources */, E92322FB9B976A926D52D12C5ECC5816 /* Switch.swift in Sources */, 1091FE5A37CC5E22263AED304386D408 /* SwitchIfEmpty.swift in Sources */, B74C2929B76A5841DBF3C73BA3C09FB8 /* SynchronizedDisposeType.swift in Sources */, 43D9EDAE797E70640D7B55EB56DA871E /* SynchronizedOnType.swift in Sources */, BC3A96095032E81BC2F8E5A479CE906E /* SynchronizedUnsubscribeType.swift in Sources */, 72AFC537FCBE9326BB279F237C9043F6 /* TailRecursiveSink.swift in Sources */, 948045455233BAFB85BDF5F033DA8686 /* Take.swift in Sources */, 5D035B296631F978D172D0AE1EDF319D /* TakeLast.swift in Sources */, CD8C4C4267E198C30ED82159B3FB2A45 /* TakeUntil.swift in Sources */, C1446EC49AB387AF0EDF8DD292AB22F3 /* TakeWhile.swift in Sources */, 03E8239071E8786ED0AFD90253814966 /* Throttle.swift in Sources */, BDB43EE666BB8F17F18E4DDBBE6C2B43 /* Timeout.swift in Sources */, 07CBA63D00073C1C74402986AE9277C3 /* Timer.swift in Sources */, C26CD78814782F6C4BF7C3B02CA06642 /* ToArray.swift in Sources */, 86C74540C24DEB52302C80CA759169C8 /* Using.swift in Sources */, 8E3D1F07AFD7234A412DAAD8E22A9A09 /* VirtualTimeConverterType.swift in Sources */, 8A25F0A06E679359184DD5C9BE408190 /* VirtualTimeScheduler.swift in Sources */, 608A03F75F4AC580AF9EBFD756AB8DC5 /* Window.swift in Sources */, 8F3F3F3792930B9DEDA544AAEA07A60A /* WithLatestFrom.swift in Sources */, DD2D9461BFEB9EE414A6D61E3A8CD7A6 /* Zip+arity.swift in Sources */, F9B8AF86D7F4375E5CDE51D06BE78501 /* Zip+Collection.swift in Sources */, 6B1D5FA190C5943AC83CBDA241C07E27 /* Zip.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; E29D37D323EFBAB92ED4EB9E402A34FF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BEE391D17D1ADB32DA7C8825FE827519 /* _RX.m in Sources */, 3DA75181D1B7D20F0CDE54EA97C3072C /* _RXDelegateProxy.m in Sources */, C1E82097CF7DF3CB4B4063ED08D334D8 /* _RXKVOObserver.m in Sources */, 4F03F93732A1DB9A6C1AD8ED0384DD90 /* _RXObjCRuntime.m in Sources */, 6E953E2A94F99E02F79C4B98004E5396 /* Bag.swift in Sources */, 5C5A43AECB27D48D5C446D3DD12DC1AD /* BehaviorRelay+Driver.swift in Sources */, FB640D6E1FCD811D905A620B508C33CE /* BehaviorRelay.swift in Sources */, 7A82F7317FDDA0BD35C5E1BD4BE42249 /* Binder.swift in Sources */, 7CD0D1E5ED33F75E019C7D85E040FED2 /* ControlEvent+Driver.swift in Sources */, F809B2628CA2797393525F37F639FB7C /* ControlEvent.swift in Sources */, 5373AA3C95048D89FB975795EA34E8C7 /* ControlProperty+Driver.swift in Sources */, 6C37C3E9F334B181ADEA8BDBF35751B5 /* ControlProperty.swift in Sources */, BD2B90D397024A00B586B7A58994F534 /* ControlTarget.swift in Sources */, 67C42ADBD42CE7F614E2893EDA641519 /* DelegateProxy.swift in Sources */, FC66B4AE1897AF442BDFF005497ECF87 /* DelegateProxyType.swift in Sources */, 9ACE31FCFAD5425ADF5B207722E20E8A /* Deprecated.swift in Sources */, 05E3461942D63CA142876829DF2178B3 /* DispatchQueue+Extensions.swift in Sources */, 643F0A0AEF6B1040E1243E8F065F0268 /* Driver+Subscription.swift in Sources */, 63C400B1C64EC85BCE1ADB5C1C869B95 /* Driver.swift in Sources */, 76407DBA37432054658558614EF3C6D5 /* InfiniteSequence.swift in Sources */, EAB9A201A0338EEBD326E78E366EEC20 /* ItemEvents.swift in Sources */, B2CFCC48BC62FB4C35F6096233613F7A /* KVORepresentable+CoreGraphics.swift in Sources */, C1E5669E219DF70E0E763DD0468795BF /* KVORepresentable+Swift.swift in Sources */, A8FC34B8104C504687DF452D1469CEC5 /* KVORepresentable.swift in Sources */, 641E1DF7AF5B9C72AAE1242C38A3F684 /* Logging.swift in Sources */, CE57D6E0880DE871AB922E0017E1472B /* NotificationCenter+Rx.swift in Sources */, 75BD25BF1A16B849D520933C732BEE8D /* NSLayoutConstraint+Rx.swift in Sources */, D3BECB43898F07D286FCEB33DC197331 /* NSObject+Rx+KVORepresentable.swift in Sources */, 9505BB9F7E1749994974BBD6D0A17CC8 /* NSObject+Rx+RawRepresentable.swift in Sources */, 99EE517D408612C2DD0FF21866BEAA63 /* NSObject+Rx.swift in Sources */, 0F7E94DE4A01E0C2DE3D8FEF7A8DA76B /* NSTextStorage+Rx.swift in Sources */, 84F11AFCD46554A1844889EBF7B915A5 /* Observable+Bind.swift in Sources */, 512C561051AAF62FEAF39708C9E8273C /* ObservableConvertibleType+Driver.swift in Sources */, 2ACB302D10B4EBDD404FE7781B7EB24D /* ObservableConvertibleType+Signal.swift in Sources */, 59B16488BD43F14B60CFEAA70A67150B /* Platform.Darwin.swift in Sources */, 2E024197B07B741D9A5BFB4CC13872FC /* Platform.Linux.swift in Sources */, 2305F81424A8EA4AC22ED71E2EE69AD1 /* PriorityQueue.swift in Sources */, D7F05F607F7BEE704777650F1F56A17D /* PublishRelay+Signal.swift in Sources */, CCBED63F97ED12D1D9F2A35E42056C7E /* PublishRelay.swift in Sources */, B3643AFA39209852E984CD30BB18413A /* Queue.swift in Sources */, C12639433CE2E21DEF3AA6DED99748FF /* RecursiveLock.swift in Sources */, 99B8666F8DEEAA2D94AF8F74CE63387B /* RxCocoa-dummy.m in Sources */, BE13FF6639C26FFBFE305F57336AB8E4 /* RxCocoa.swift in Sources */, 356CB228C18677929A9A520E77620B13 /* RxCocoaObjCRuntimeError+Extensions.swift in Sources */, 9E04C84E074D52269B2611658420FD41 /* RxCollectionViewDataSourceProxy.swift in Sources */, 7F61C5C04977D2A2FF71DF6FEC861A94 /* RxCollectionViewDataSourceType.swift in Sources */, B1C6D346149535E8CA5CD2443BBFCCC3 /* RxCollectionViewDelegateProxy.swift in Sources */, CDFA17536632AE886DF2DD7FCBFF8F68 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, 8E6BFD0EC4206F98140D4CA51D60D517 /* RxNavigationControllerDelegateProxy.swift in Sources */, 788910D5C544192C236243BF21CD1E78 /* RxPickerViewAdapter.swift in Sources */, F98CB7FADDB6FDFD66E71D447444F5C5 /* RxPickerViewDataSourceProxy.swift in Sources */, D044C46E53776D7072DB89497EC1AC73 /* RxPickerViewDataSourceType.swift in Sources */, E454C46205FD054E2133897B1CF339D8 /* RxPickerViewDelegateProxy.swift in Sources */, BE7C78E3DD8046E3CB4BC5A28C057853 /* RxScrollViewDelegateProxy.swift in Sources */, 25729D655583EBF1BDE99FF6BEAC272C /* RxSearchBarDelegateProxy.swift in Sources */, 8D0EC20E5A152C52E877A0FD42A1D612 /* RxSearchControllerDelegateProxy.swift in Sources */, 377636521A7593EE0D162CA57DD21C2A /* RxTabBarControllerDelegateProxy.swift in Sources */, EDA4B2824B12E7A3579F99909744E23C /* RxTabBarDelegateProxy.swift in Sources */, 111388F3289E572B7FF90312D3C23DD1 /* RxTableViewDataSourceProxy.swift in Sources */, 8014530DA6E561B4CA4905F2F68FF87C /* RxTableViewDataSourceType.swift in Sources */, 6AE889190D5DE14192E8E0D26E0EA3B9 /* RxTableViewDelegateProxy.swift in Sources */, 35CE9431B032341D2B6C4FF4384C84AB /* RxTableViewReactiveArrayDataSource.swift in Sources */, AF5D443F3631C6B95B4E8F831DCFDA79 /* RxTarget.swift in Sources */, 93A094FED3649B43022FAD6174FBF11F /* RxTextStorageDelegateProxy.swift in Sources */, 23BE46034D9F742CFFED1D6B7A25A2E1 /* RxTextViewDelegateProxy.swift in Sources */, 7C98D625AAB36D8F2A92D5FAF28FCCF1 /* RxWebViewDelegateProxy.swift in Sources */, 7B50A3CE4086B03F60DCAFD8090DF164 /* SchedulerType+SharedSequence.swift in Sources */, 7DF2FFD108A489BAE76957B27C0815F2 /* SectionedViewDataSourceType.swift in Sources */, 608E9ABBC7B02657C21A181EC25DED65 /* SharedSequence+Operators+arity.swift in Sources */, 5759B5BC5FA1E2C7B5FF69B511DF68DA /* SharedSequence+Operators.swift in Sources */, 43B97E40D05359125AA9EB9A928D53C7 /* SharedSequence.swift in Sources */, DB2D2DBE410D12B4A8EC0DEFACF3DF32 /* Signal+Subscription.swift in Sources */, 9983C08AB4621CB25D31ED64E3005A86 /* Signal.swift in Sources */, 06CCF3314F269406C8705B30D86CFD7C /* TextInput.swift in Sources */, F71155830BCEEE6D69E12F14089ED538 /* UIActivityIndicatorView+Rx.swift in Sources */, FB27B9C1DE7B9384AB45D8BBDECD8034 /* UIAlertAction+Rx.swift in Sources */, 339CF382B95EBAEAC534A4DB86412D49 /* UIApplication+Rx.swift in Sources */, D7B62E5B90F5CC105525D4E4A710A1F3 /* UIBarButtonItem+Rx.swift in Sources */, CE7953BA2996D7B543684441E7BF2579 /* UIButton+Rx.swift in Sources */, 0DF6F077A01F83D2296F474A6657D3DA /* UICollectionView+Rx.swift in Sources */, 94E7616A9A6BBA3C0C59BE9F694AD549 /* UIControl+Rx.swift in Sources */, 8EA1993902CC8DE0A3E5F00E00976C4D /* UIDatePicker+Rx.swift in Sources */, E678304A73835950C93570CD42BF160F /* UIGestureRecognizer+Rx.swift in Sources */, 267F1D36C8E6ED6B5CBD0DACA0FB38C3 /* UIImageView+Rx.swift in Sources */, 80B0A06B1530CD62C0E818A58D30BCF8 /* UILabel+Rx.swift in Sources */, 677CCFAA3FB253A30288AF00D9C792AF /* UINavigationController+Rx.swift in Sources */, 1248CDC903C16DF981D48B43CC8CFDC5 /* UINavigationItem+Rx.swift in Sources */, 2C5FA28BC6344178AFF753F9BEEC172F /* UIPageControl+Rx.swift in Sources */, A32920098427AC9AA08B59797AAB0AA0 /* UIPickerView+Rx.swift in Sources */, 2738EFBB546EAD1A60E9426A13D68CEB /* UIProgressView+Rx.swift in Sources */, 0A936243CBB929114D3405F0AC7C234F /* UIRefreshControl+Rx.swift in Sources */, EF298B1BEAB1956FCF7E1E8EA3911780 /* UIScrollView+Rx.swift in Sources */, 1F242D91138AD1C8F49DCDA41B71461F /* UISearchBar+Rx.swift in Sources */, B3BC5178332C6FA8017CB00559E2AD92 /* UISearchController+Rx.swift in Sources */, E47F2B7C9621969D29330632BF46DFE3 /* UISegmentedControl+Rx.swift in Sources */, C1F06F8B94A566ABE591F84CEAC03C14 /* UISlider+Rx.swift in Sources */, 2A194A7EBF3AF8D602791B5686534941 /* UIStepper+Rx.swift in Sources */, C7298FEC2DD0BCFDF12F89121CA036A7 /* UISwitch+Rx.swift in Sources */, 7C1AFD71632F9548563C3B224B6BFAF1 /* UITabBar+Rx.swift in Sources */, 902C28D80B247A6737276BF247BE1D1A /* UITabBarController+Rx.swift in Sources */, 59BC49B15C7B4BD0AA38436D3567F9BD /* UITabBarItem+Rx.swift in Sources */, F66F21FCAD37F04AF3CE0B9ADDFACCC3 /* UITableView+Rx.swift in Sources */, 1991BB121497AC9447A5DC6445464176 /* UITextField+Rx.swift in Sources */, 5C6EBB68141BE6FE8A58CDAA7456BB67 /* UITextView+Rx.swift in Sources */, 1ED1D03FFEB9595492A4F8783D35F81C /* UIView+Rx.swift in Sources */, 8DE01434EBF2D9040CF7BA7CBE33DD6C /* UIViewController+Rx.swift in Sources */, 8151252BABBE1D0CF4828711B003BCE6 /* UIWebView+Rx.swift in Sources */, 8FB1637E28C8213F6A57DFBFC5390CAC /* URLSession+Rx.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F35FEC988ECD73463D93176BC71A43B1 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0F981C56836FE34E921BC7253826A170 /* CustomDateFormatTransform.swift in Sources */, EC1A896625476730D64BF9645624BBAD /* DataTransform.swift in Sources */, 4215D7313CE6E738AAD80E9FEC6C60CC /* DateFormatterTransform.swift in Sources */, F9B63BDA169DB048D41DA8395261AEBA /* DateTransform.swift in Sources */, B2EFEFFB34D517F8E9A203B674AEBD13 /* DictionaryTransform.swift in Sources */, 706840688BA7E9BDE5F4DD2C0E558835 /* EnumOperators.swift in Sources */, FCE734BD83FB03D3DC9F8AA843ECD73B /* EnumTransform.swift in Sources */, DB87FF0D3B17ABB5452FDD4D70C9A84D /* FromJSON.swift in Sources */, 551FB4DF0E5072BB849BB4FCE74F7361 /* HexColorTransform.swift in Sources */, B9C39C539A810D1958AEEF1AEC0FD75C /* ImmutableMappable.swift in Sources */, 1894026C296B8A77711607C98ED40C86 /* IntegerOperators.swift in Sources */, 255C4ADB2BD82FEEF86A3D4D43F31C63 /* ISO8601DateTransform.swift in Sources */, A5BFC49ED41BC416BEBF4772517AB741 /* Map.swift in Sources */, 2D89AB26203628C04B3E87D045525701 /* MapError.swift in Sources */, 67A4D8A24085ADC44A65404E73461B43 /* Mappable.swift in Sources */, 77CA4CD47748C0B69982A6D82C2799D5 /* Mapper.swift in Sources */, 6C2FE27FDBD6621AAB57E4082419C347 /* NSDecimalNumberTransform.swift in Sources */, 59109302859941E04E87DC7044DFEE89 /* ObjectMapper-dummy.m in Sources */, EA67527CAF7ED571C8A84B66DDD70FBE /* Operators.swift in Sources */, 134859C42E05396F62489E4329ECF6EA /* ToJSON.swift in Sources */, 194858D10B9B1DD9FFEFBE4EAA0700F3 /* TransformOf.swift in Sources */, 18E9B5E96557418567FCB7DCC559299F /* TransformOperators.swift in Sources */, 27156C9092B7A82E24CF96312A941150 /* TransformType.swift in Sources */, F527B0A072DFD754B471E4C1F6B16C48 /* URLTransform.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F68621466263ABC9C173B16DCEA5238C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 28766FD0E20C59920C9235FF0BCB22A2 /* Then-dummy.m in Sources */, 4A79D8BBA62B2581089376DCF16AFD95 /* Then.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F7E49EE33EA82533063B0336FC00338E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( B8567C825865356AF4137382CE6C007F /* Pods-RxXMLY-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F81A606714EAF37F04ADC51B6B6FD751 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 481AB919527D0B7F4C6DBC8F84DBCDE0 /* TYPagerController-dummy.m in Sources */, 0A10103215004C9822201FB9572B2C00 /* TYPagerController.m in Sources */, B5A770DCA3CD54A8B48F6166F9D440E2 /* TYPagerView.m in Sources */, AACE8D1BD32A4820B7BEB0EE91FB0795 /* TYPagerViewLayout.m in Sources */, 9F75265E3A28C06EA75E5FEF00087551 /* TYTabPagerBar.m in Sources */, ACCE7F2F7E9C3F68256E49D4654E6076 /* TYTabPagerBarCell.m in Sources */, E1ED57DFDC84B588DCFF41921D9BED15 /* TYTabPagerBarLayout.m in Sources */, 1314F3F62B92768E16CBFF1490AF29DE /* TYTabPagerController.m in Sources */, B0B357445D024E50F897E2DE8F2D58AF /* TYTabPagerView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; FD12B015E1FDA3498428EF09C91D1F17 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( A7EC09EC6EFAB3778E0A0341E7DC9CE3 /* AnimatedImageView.swift in Sources */, B0033530A0EAABBB31CD46CA2899F3BD /* Box.swift in Sources */, 5D83206A9A4656F11D511158EC8E0784 /* CacheSerializer.swift in Sources */, 1DAF3BB88351664576507F774FEB051C /* Filter.swift in Sources */, 40765AAA791D6DFC0022639E348F0E6A /* FormatIndicatedCacheSerializer.swift in Sources */, 950D8E334E011B13CC5786595E6AE615 /* Image.swift in Sources */, 8D7D42C25F9430F572E14200C399D57B /* ImageCache.swift in Sources */, 413C2A3759ABEC870AAE61F28C258C31 /* ImageDownloader.swift in Sources */, F99792883819ADBD16123F91F6A9F3F8 /* ImagePrefetcher.swift in Sources */, 93D444FC9D79520E14FB8EF178F0B76E /* ImageProcessor.swift in Sources */, 07432E8DEA8B25795C7C545AA67A0187 /* ImageTransition.swift in Sources */, CB8DF027448816C623E9B02A3685A59F /* ImageView+Kingfisher.swift in Sources */, 2082EB4AA16440FFD5479FAFC0181066 /* Indicator.swift in Sources */, CB4222BC34D92C2E06A5BCF730FEFE71 /* Kingfisher-dummy.m in Sources */, 873BA68C2E24D634D38E4A8BCB74B08D /* Kingfisher.swift in Sources */, 383C9A8DB84B83233633C3A00A2C1608 /* KingfisherManager.swift in Sources */, 1827E7D94F3A12CA0C58EF33AE4B026D /* KingfisherOptionsInfo.swift in Sources */, E710FF1F4EB4B125F9C5DBDFE3B9320F /* Placeholder.swift in Sources */, 282E6F0E67F3799B0ABCB9ED96B666A9 /* RequestModifier.swift in Sources */, 1FCC0970C8435F10768CA84C2025A177 /* Resource.swift in Sources */, 10AD5E70FDD53228153032C58FBF4D52 /* String+MD5.swift in Sources */, 48EC78A50C61424D4DFDC33A325CAA72 /* ThreadHelper.swift in Sources */, FAC3C67720C7E6EF51CCAE58F62645BC /* UIButton+Kingfisher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 08BBAECE794424C6A743B64C09DB1F15 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = SnapKit; target = 8E38AFA40EB24AAD3998F3F9CFB12CA3 /* SnapKit */; targetProxy = 2ACD2B5E0B881C8AE65F933BD84DB2BB /* PBXContainerItemProxy */; }; 10AED09D93D2CD014F7BA195EFC2A01D /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = TYCyclePagerView; target = 0B620384669DABB34C3C5B96EA60F12A /* TYCyclePagerView */; targetProxy = FC9E7142C6CF7EE64F13E5BEE4B234E1 /* PBXContainerItemProxy */; }; 1305CEBDCD0AA3A968FBB90778AC74B5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = 1B9585FC3126D0BD21B2221D625803E7 /* PBXContainerItemProxy */; }; 1D6CC51BC907D8D84EC14CFC3A935BF9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = SwiftyColor; target = 93FD28A3DC61A9094248D0D96CAD9FD4 /* SwiftyColor */; targetProxy = 6CEBF919355CD7B687C5D986511A3630 /* PBXContainerItemProxy */; }; 1F022C3FAE5D080B47B2C4EB5A4B7A31 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxCocoa; target = 1F5BFD2D8E3672CE19EA16B5208A4066 /* RxCocoa */; targetProxy = 8DE395CD515C5D9F68A4FBEC6D9E49FB /* PBXContainerItemProxy */; }; 2217DC3041FFBBE5E80AAB2A9DC3C930 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Alamofire; target = 88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */; targetProxy = C291EBE2993E897C362199B1BAEEC035 /* PBXContainerItemProxy */; }; 262BA16A4ED2BE76F6D01B3E3A3ECD1B /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = FEA62B82099C97A42F3968BAAD232850 /* PBXContainerItemProxy */; }; 41F1B2694597D80C8C88DD1E191C2DA0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxCocoa; target = 1F5BFD2D8E3672CE19EA16B5208A4066 /* RxCocoa */; targetProxy = A344436CFCCEA3FD967A5C394C074F4A /* PBXContainerItemProxy */; }; 4979E917B04F9F132A2DEC8E47979A28 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Differentiator; target = 844C9B244CEFA2F26288789C423101B4 /* Differentiator */; targetProxy = 15F1D0A9F74EAF7D407DC563D31027A7 /* PBXContainerItemProxy */; }; 49EE20815EBE4FABDDC3320C6B5B116C /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = 3AEB60BF032C5F152E4CCAEF2273CF19 /* PBXContainerItemProxy */; }; 4A1836A5702E2F2BF9536F435BCC4E92 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = TYPagerController; target = CCEEA2E517C9CB166C75BAF4A263A7CF /* TYPagerController */; targetProxy = B5DD30A0B9289F764F95945D2D1F8C8B /* PBXContainerItemProxy */; }; 4C8BC7A07FCDEC22BAC47D2EA5B8DB58 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = ReusableKit; target = 524DC20BC58D4BDA1F0C77763DFA70BE /* ReusableKit */; targetProxy = A7230AE231CD9B06EE4CDAE3A1068E9D /* PBXContainerItemProxy */; }; 4EB5F10F6AC0963099F3FC814DF14415 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Result; target = 1C61647CEE01897368F5D0D2CC181DB4 /* Result */; targetProxy = F78D05F575692EA731781B820137FCAC /* PBXContainerItemProxy */; }; 5444B96DBCDAD467604CD8796C2A3AE2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = A7D03FCA25AACE15F81E2A98FE9DC3D8 /* PBXContainerItemProxy */; }; 564A6BC33E46BA3758AE193165BD2546 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxCocoa; target = 1F5BFD2D8E3672CE19EA16B5208A4066 /* RxCocoa */; targetProxy = 5421BFF2151DBD1E4557434A332B44FF /* PBXContainerItemProxy */; }; 74F8F17B6FC071183EB1EC5D2ED0880E /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = 23784648AAF575118683F20D78A60594 /* PBXContainerItemProxy */; }; 7A1A38F35129D8939DA93941E5566EFE /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = IQKeyboardManagerSwift; target = C235F77E101E742652CCF98FFDFA5182 /* IQKeyboardManagerSwift */; targetProxy = A2D41E2E3A703CF7EC447F13B1D2D28D /* PBXContainerItemProxy */; }; 7DF9738D2EB15BE150084534280EEDBC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxGesture; target = B496A58A7B812A10E0039E15EDF0E8EE /* RxGesture */; targetProxy = D7D399EDABE3916260C38946E0F4CCDD /* PBXContainerItemProxy */; }; 7E2C13AE966FB76B2736F2F745A55A84 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Differentiator; target = 844C9B244CEFA2F26288789C423101B4 /* Differentiator */; targetProxy = 6261BDDB54F9C51D2C747DE59337227D /* PBXContainerItemProxy */; }; 7F53856AB469B6C0D0227D2098D36F1A /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Result; target = 1C61647CEE01897368F5D0D2CC181DB4 /* Result */; targetProxy = 9A4961597B182A9A8365503B12A4CC49 /* PBXContainerItemProxy */; }; 82A7E8FA790BFD2EEAA4CBBFC398A3AD /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Alamofire; target = 88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */; targetProxy = 9264E39D9FF475826031CA00737738D8 /* PBXContainerItemProxy */; }; 91360C912E71D51EA842B318BAEBA511 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Alamofire; target = 88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */; targetProxy = 80D52ABA9C03D8C4E466A3C2ECC923CF /* PBXContainerItemProxy */; }; 994AD4F34D30D7AD6C8E508F85E720FC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "NSObject+Rx"; target = 64CC9E274EBD6C16C53FC82EAA6FE9E7 /* NSObject+Rx */; targetProxy = 1A8E520D805AA6588B14DC77528D8B0F /* PBXContainerItemProxy */; }; A5495B347F8F13816C9E988F7ED6E887 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxCocoa; target = 1F5BFD2D8E3672CE19EA16B5208A4066 /* RxCocoa */; targetProxy = B40C0E974B9564F38F538252683B1ABC /* PBXContainerItemProxy */; }; A9111896C809CC9110D085304115B040 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = C1809C26AE9AE581B69F3EBF3020386B /* PBXContainerItemProxy */; }; AE2A28A9913F488DA60A2AE400C7C39C /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Kingfisher; target = 6F712943B3C5592E82604940D11CE08A /* Kingfisher */; targetProxy = 6BF334AD50FD77D652D687BCC4AEE011 /* PBXContainerItemProxy */; }; AFCAAA9C845955F183FAC004DE305241 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = MJRefresh; target = E14A62900910444707A5504D25EC98DC /* MJRefresh */; targetProxy = 2BA940D9885781F490DE5D7E8AF779C7 /* PBXContainerItemProxy */; }; B9039DCFEEA782D86F9244953C22F447 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxDataSources; target = F05F33A12963EC1A726FEE15D9000AE2 /* RxDataSources */; targetProxy = B6AEA89E672C6C293F8A451E760DC0B9 /* PBXContainerItemProxy */; }; C04FA726205A1D05F027F1208191E76E /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = 51642FCC15CB2105630B74B65E122C2C /* PBXContainerItemProxy */; }; C0DCC3CCC842C93357AEFDB929B5A252 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = ObjectMapper; target = CD6C9FB6B052E1CF663F8A56041EA163 /* ObjectMapper */; targetProxy = AEB2857C08385E6AA8DBB9F8736DAF9F /* PBXContainerItemProxy */; }; C2A6A892DEAC32783AE9D267A1E58534 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = ReactorKit; target = 0F6A8E28D6C2CC2BCA0477C6AF7137F2 /* ReactorKit */; targetProxy = 338578A6E2DF40FE81DEC3B1372FB4AC /* PBXContainerItemProxy */; }; D56703EF22DD8F7706F17918A2D0AB7F /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Then; target = 2D5C20681FC0A1E18576D4673D0EFE65 /* Then */; targetProxy = DCDD2C4EA666E90B5DDFEEBBA1DC5C8F /* PBXContainerItemProxy */; }; D957D03022753D4ACA9E09D332D8EEA5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxSwift; target = 371551DFD98A9103D43168E4BD0479F0 /* RxSwift */; targetProxy = FF663F02F48AB40164A87D8914B81DF4 /* PBXContainerItemProxy */; }; DAB919EDAE0C8E3D7232AFB62120664E /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = SwiftyJSON; target = DE874A99677D06A11E5FCC43C238A7D5 /* SwiftyJSON */; targetProxy = E6C01306761AF7F0DB6473C9C03ECFBD /* PBXContainerItemProxy */; }; DABB17D04DB056ACFEE47ED47B21D92E /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RxAlamofire; target = 2166B105EDC9D2499513E6D6BCD586C2 /* RxAlamofire */; targetProxy = E09A273BEF2F739232571736BCC24375 /* PBXContainerItemProxy */; }; EEF9A4EC5A5005C3AED3CBD2E4BF7DD3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = TTRangeSlider; target = E86A9F4C831EEE122A82C7CE19D4011B /* TTRangeSlider */; targetProxy = 9B2B284C2F27B08C57FFC9EE798CE2AB /* PBXContainerItemProxy */; }; F5E4336F7DB15BFD3731353A54108BFC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = URLNavigator; target = C76585AF9502709B32A11133189DDA23 /* URLNavigator */; targetProxy = 9FCB8BF28633D811B92390B57E58E0F8 /* PBXContainerItemProxy */; }; FD527F145232A0C224EE6A40F67AB20C /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Moya; target = D382A7B90B59D72908CC21054785B020 /* Moya */; targetProxy = C8D1C0BFABDA878088C177C8EE78FF8A /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 0042C2BDBEB9CD6E7C180ACEDF7F3230 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = DB2A4AE8B923CFBB0D0531CB91C712CE /* MJRefresh.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/MJRefresh/MJRefresh-prefix.pch"; INFOPLIST_FILE = "Target Support Files/MJRefresh/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/MJRefresh/MJRefresh.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = MJRefresh; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 0149886A2ED9ED38B90DCC0CB07F5228 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 319F0938C83A11D7010E8A6B1A8BC27E /* ReactorKit.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/ReactorKit/ReactorKit-prefix.pch"; INFOPLIST_FILE = "Target Support Files/ReactorKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/ReactorKit/ReactorKit.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = ReactorKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 0667D7072671234E32C33BAE8737DA70 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 268594CEB578B4D18F4F1B30652F617B /* URLNavigator.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/URLNavigator/URLNavigator-prefix.pch"; INFOPLIST_FILE = "Target Support Files/URLNavigator/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/URLNavigator/URLNavigator.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = URLNavigator; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 07E925C481DDD32B742C8B0D765D951D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGNING_REQUIRED = NO; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "POD_CONFIGURATION_RELEASE=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.1; PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; STRIP_INSTALLED_PRODUCT = NO; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SYMROOT = "${SRCROOT}/../build"; VALIDATE_PRODUCT = YES; }; name = Release; }; 086A5FDC58C99AFB20FA815843EE41DE /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 41B3ACB613105C496C9AA802C306A439 /* RxGesture.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxGesture/RxGesture-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxGesture/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxGesture/RxGesture.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = RxGesture; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 0A523BF1DE09BDF5626D574CC59B658E /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9CEA02DF9281493AB02811A381B532ED /* TYCyclePagerView.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/TYCyclePagerView/TYCyclePagerView-prefix.pch"; INFOPLIST_FILE = "Target Support Files/TYCyclePagerView/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/TYCyclePagerView/TYCyclePagerView.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = TYCyclePagerView; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 10081673734687833D9A3EE73A4C28AA /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 41B3ACB613105C496C9AA802C306A439 /* RxGesture.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxGesture/RxGesture-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxGesture/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxGesture/RxGesture.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = RxGesture; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 165696DD5DC4E8A99EB8459726B5F42E /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 4E295D0CB7D2F465413AC6086CBF8C49 /* SwiftyJSON.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = SwiftyJSON; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 211EA9C8BE0D10435468E86A282A9AD9 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8D3F2B09E35E2D2EFA58CB9D9261E7D0 /* NSObject+Rx.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/NSObject+Rx/NSObject+Rx-prefix.pch"; INFOPLIST_FILE = "Target Support Files/NSObject+Rx/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/NSObject+Rx/NSObject+Rx.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = NSObject_Rx; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 23E8F2106453B25411BBD59AD634697F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = E168570235239A51FCD669B5E01BD944 /* TTRangeSlider.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/TTRangeSlider/TTRangeSlider-prefix.pch"; INFOPLIST_FILE = "Target Support Files/TTRangeSlider/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/TTRangeSlider/TTRangeSlider.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = TTRangeSlider; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 2529F6EDCBF2615DE21E47D137D4283B /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 50A9B85F6C0935020B51702CEF26AC84 /* TYPagerController.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/TYPagerController/TYPagerController-prefix.pch"; INFOPLIST_FILE = "Target Support Files/TYPagerController/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/TYPagerController/TYPagerController.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = TYPagerController; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 29804FEF0B8246EFF6ED65D0F799AEA3 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9D52DB027375B4BF6987825A6BF5751B /* RxAlamofire.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxAlamofire/RxAlamofire-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxAlamofire/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxAlamofire/RxAlamofire.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = RxAlamofire; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 2E15E9C97438ACF0A953E95909C87920 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8D9FE6C7F2AC2C3319C58D4272FD52C1 /* IQKeyboardManagerSwift.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch"; INFOPLIST_FILE = "Target Support Files/IQKeyboardManagerSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = IQKeyboardManagerSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 33E464B02591D1161D80223BA28CA6F7 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 82C7B1F1647EBCD9A81E7A2788701631 /* SnapKit.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/SnapKit/SnapKit-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SnapKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SnapKit/SnapKit.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = SnapKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 34DFB997862DE04F29457821E6B6FD62 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 4E295D0CB7D2F465413AC6086CBF8C49 /* SwiftyJSON.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = SwiftyJSON; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 34F671C601072D988FC80B795FE6B097 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = A222328DE93D544E711805351EB49909 /* RxDataSources.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxDataSources/RxDataSources-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxDataSources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxDataSources/RxDataSources.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = RxDataSources; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 3D87FE564C17E455688A363472861A61 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 28FD4835CF32D5E0B1A2A3753A09DBB0 /* Moya.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Moya/Moya-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Moya/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Moya/Moya.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = Moya; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 441300C5FC4B5A08DA02C97A98A9AC30 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9855A9C6CDDA35414C161F75BED8B6F8 /* Alamofire.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = Alamofire; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 45A8AEE01E68294DAB97CD528C053793 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 40C89EACDC9DB733AEDA2C1858349B05 /* ObjectMapper.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/ObjectMapper/ObjectMapper-prefix.pch"; INFOPLIST_FILE = "Target Support Files/ObjectMapper/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/ObjectMapper/ObjectMapper.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = ObjectMapper; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 4C06BA85D970A101C292FA1B42B916A0 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 716F007B040C0CDD77F14CF5209FEE01 /* Pods-RxXMLY.debug.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "Target Support Files/Pods-RxXMLY/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-RxXMLY/Pods-RxXMLY.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = Pods_RxXMLY; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 4C8DEDD19177C04E0FAFD07889C79A77 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = E168570235239A51FCD669B5E01BD944 /* TTRangeSlider.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/TTRangeSlider/TTRangeSlider-prefix.pch"; INFOPLIST_FILE = "Target Support Files/TTRangeSlider/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/TTRangeSlider/TTRangeSlider.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = TTRangeSlider; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 536A4065D0055B28FCC2536BA827FBC6 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 50A9B85F6C0935020B51702CEF26AC84 /* TYPagerController.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/TYPagerController/TYPagerController-prefix.pch"; INFOPLIST_FILE = "Target Support Files/TYPagerController/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/TYPagerController/TYPagerController.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = TYPagerController; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 57A92DFDF0A03B32F5C887C772B20A22 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = EC5D50260AEE66CC8FD97C3CF9E10E7F /* Then.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Then/Then-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Then/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Then/Then.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = Then; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 5E7A444B4E19CE3ABF2C4097953E4C54 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = DB2A4AE8B923CFBB0D0531CB91C712CE /* MJRefresh.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/MJRefresh/MJRefresh-prefix.pch"; INFOPLIST_FILE = "Target Support Files/MJRefresh/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/MJRefresh/MJRefresh.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = MJRefresh; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 6292F718E229B9D83E841718720DD31B /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 0A8EF0EB23267E967663F5FA5A8E7686 /* RxSwift.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxSwift/RxSwift-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxSwift/RxSwift.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = RxSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 65AEE624D7D47405E3498E228DB2FDAA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGNING_REQUIRED = NO; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "POD_CONFIGURATION_DEBUG=1", "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.1; ONLY_ACTIVE_ARCH = YES; PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; STRIP_INSTALLED_PRODUCT = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SYMROOT = "${SRCROOT}/../build"; }; name = Debug; }; 68875726E361E4D5EE55450A0565AE5E /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 28FD4835CF32D5E0B1A2A3753A09DBB0 /* Moya.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Moya/Moya-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Moya/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Moya/Moya.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = Moya; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 694AA0C88B645D5A0D978E74AA494B72 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 02242206E1BBE2B18A7CA031B7102D98 /* Differentiator.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Differentiator/Differentiator-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Differentiator/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Differentiator/Differentiator.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = Differentiator; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 6BBACD06F1E5984EAC3428F9093A8573 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = B013E2FF6D44578E3E483B417504DD8F /* ReusableKit.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/ReusableKit/ReusableKit-prefix.pch"; INFOPLIST_FILE = "Target Support Files/ReusableKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/ReusableKit/ReusableKit.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = ReusableKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 78F2DC967C87777534BEEC714E8BD1E0 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9D52DB027375B4BF6987825A6BF5751B /* RxAlamofire.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxAlamofire/RxAlamofire-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxAlamofire/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxAlamofire/RxAlamofire.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = RxAlamofire; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 7B4DBB05E401008022849E9B6A9AF714 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AA6F3E86B6FCC86A1E5A52F243EE56D /* SwiftyColor.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/SwiftyColor/SwiftyColor-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SwiftyColor/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SwiftyColor/SwiftyColor.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = SwiftyColor; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 81E3EC0CBC30A01E8D29789CFAE9F399 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 3F697E60BD60ED1A4604DA7BF8581AB1 /* Pods-RxXMLY.release.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "Target Support Files/Pods-RxXMLY/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-RxXMLY/Pods-RxXMLY.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = Pods_RxXMLY; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 8B75AF8EE7468D85BDC95BF5356A6B48 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 0A8EF0EB23267E967663F5FA5A8E7686 /* RxSwift.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxSwift/RxSwift-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxSwift/RxSwift.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = RxSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 93374469E11255902CAA7DD4CB27A692 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 40C89EACDC9DB733AEDA2C1858349B05 /* ObjectMapper.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/ObjectMapper/ObjectMapper-prefix.pch"; INFOPLIST_FILE = "Target Support Files/ObjectMapper/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/ObjectMapper/ObjectMapper.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = ObjectMapper; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 9359037A8F209948A0C05BE187F3F89F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8A5ABA18A2D1A61A185ED12DF70B8F30 /* Result.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Result/Result-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Result/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Result/Result.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = Result; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 93D684927DE728E6F82AB56702FC59F2 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = B6DD7C62D3975725987453F154E89E03 /* Kingfisher.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = Kingfisher; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 9DDE49D82AFCF60FECAA422AA55ED0E6 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AA6F3E86B6FCC86A1E5A52F243EE56D /* SwiftyColor.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/SwiftyColor/SwiftyColor-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SwiftyColor/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SwiftyColor/SwiftyColor.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = SwiftyColor; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; A46F53241451B8AA7DF21F81C4C1EE1B /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = A222328DE93D544E711805351EB49909 /* RxDataSources.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxDataSources/RxDataSources-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxDataSources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxDataSources/RxDataSources.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = RxDataSources; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; A516FB1A3CF0C0E38E6E12023BE62772 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9855A9C6CDDA35414C161F75BED8B6F8 /* Alamofire.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = Alamofire; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; A9B73D0C39ADC5559CCF5B20A1CE4053 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8D3F2B09E35E2D2EFA58CB9D9261E7D0 /* NSObject+Rx.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/NSObject+Rx/NSObject+Rx-prefix.pch"; INFOPLIST_FILE = "Target Support Files/NSObject+Rx/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/NSObject+Rx/NSObject+Rx.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = NSObject_Rx; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; BAF9A20A5B0B25A6F56D6CF4C427B72F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 319F0938C83A11D7010E8A6B1A8BC27E /* ReactorKit.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/ReactorKit/ReactorKit-prefix.pch"; INFOPLIST_FILE = "Target Support Files/ReactorKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/ReactorKit/ReactorKit.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = ReactorKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; BB895A8B8C5E6F6144E3E40F6BDE4051 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 02242206E1BBE2B18A7CA031B7102D98 /* Differentiator.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Differentiator/Differentiator-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Differentiator/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Differentiator/Differentiator.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = Differentiator; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; C4DDADE7FC0CC645D75299F75A9ACF65 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9CEA02DF9281493AB02811A381B532ED /* TYCyclePagerView.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/TYCyclePagerView/TYCyclePagerView-prefix.pch"; INFOPLIST_FILE = "Target Support Files/TYCyclePagerView/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/TYCyclePagerView/TYCyclePagerView.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = TYCyclePagerView; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; CB6CE1072EC77BB7ADBBC0F4F6A24078 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 351B9B4056B3818966E04C76590FC3B7 /* RxCocoa.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxCocoa/RxCocoa-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxCocoa/RxCocoa.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = RxCocoa; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; D142C161ECF4956E8ADB991690FCAD39 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 351B9B4056B3818966E04C76590FC3B7 /* RxCocoa.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/RxCocoa/RxCocoa-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/RxCocoa/RxCocoa.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = RxCocoa; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; D88A3909EE1F5CD04C59F0E32C9B8592 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8D9FE6C7F2AC2C3319C58D4272FD52C1 /* IQKeyboardManagerSwift.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch"; INFOPLIST_FILE = "Target Support Files/IQKeyboardManagerSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = IQKeyboardManagerSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; D8EA526A6238854CEBFF0440AE6D0BEF /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = EC5D50260AEE66CC8FD97C3CF9E10E7F /* Then.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Then/Then-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Then/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Then/Then.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = Then; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; E32C7F392C5CF839D49F222FB81B23A0 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = B013E2FF6D44578E3E483B417504DD8F /* ReusableKit.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/ReusableKit/ReusableKit-prefix.pch"; INFOPLIST_FILE = "Target Support Files/ReusableKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/ReusableKit/ReusableKit.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = ReusableKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; EB18C0ABDA9FBD6260148AE14861F47D /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8A5ABA18A2D1A61A185ED12DF70B8F30 /* Result.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Result/Result-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Result/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Result/Result.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = Result; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; EFD3CA7A881278528426A95F79CD1C07 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 82C7B1F1647EBCD9A81E7A2788701631 /* SnapKit.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/SnapKit/SnapKit-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SnapKit/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SnapKit/SnapKit.modulemap"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = SnapKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; F277A68C2ED70936C636EC5B204AB50E /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 268594CEB578B4D18F4F1B30652F617B /* URLNavigator.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/URLNavigator/URLNavigator-prefix.pch"; INFOPLIST_FILE = "Target Support Files/URLNavigator/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/URLNavigator/URLNavigator.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = URLNavigator; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; F65473631C6DA6653F95FF57B7FCB48C /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = B6DD7C62D3975725987453F154E89E03 /* Kingfisher.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = Kingfisher; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 0C3A2D1670AC6E68C80F257B426F3A29 /* Build configuration list for PBXNativeTarget "TTRangeSlider" */ = { isa = XCConfigurationList; buildConfigurations = ( 23E8F2106453B25411BBD59AD634697F /* Debug */, 4C8DEDD19177C04E0FAFD07889C79A77 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 0D42361BF39A06A081758B64C4D8FB8D /* Build configuration list for PBXNativeTarget "ReusableKit" */ = { isa = XCConfigurationList; buildConfigurations = ( 6BBACD06F1E5984EAC3428F9093A8573 /* Debug */, E32C7F392C5CF839D49F222FB81B23A0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1EFADDC2BB6831ED43B76E0241CCDB75 /* Build configuration list for PBXNativeTarget "MJRefresh" */ = { isa = XCConfigurationList; buildConfigurations = ( 5E7A444B4E19CE3ABF2C4097953E4C54 /* Debug */, 0042C2BDBEB9CD6E7C180ACEDF7F3230 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 23CFD86DE455F2B998EC09D589DA285D /* Build configuration list for PBXNativeTarget "ObjectMapper" */ = { isa = XCConfigurationList; buildConfigurations = ( 45A8AEE01E68294DAB97CD528C053793 /* Debug */, 93374469E11255902CAA7DD4CB27A692 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { isa = XCConfigurationList; buildConfigurations = ( 65AEE624D7D47405E3498E228DB2FDAA /* Debug */, 07E925C481DDD32B742C8B0D765D951D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 3371F7E9117E31C177556E75F3A0EBB0 /* Build configuration list for PBXNativeTarget "Pods-RxXMLY" */ = { isa = XCConfigurationList; buildConfigurations = ( 4C06BA85D970A101C292FA1B42B916A0 /* Debug */, 81E3EC0CBC30A01E8D29789CFAE9F399 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 419E5D95491847CD79841B971A8A3277 /* Build configuration list for PBXNativeTarget "Alamofire" */ = { isa = XCConfigurationList; buildConfigurations = ( A516FB1A3CF0C0E38E6E12023BE62772 /* Debug */, 441300C5FC4B5A08DA02C97A98A9AC30 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 512FD626A7E3FC83C9CC8A24144BC92B /* Build configuration list for PBXNativeTarget "SwiftyColor" */ = { isa = XCConfigurationList; buildConfigurations = ( 9DDE49D82AFCF60FECAA422AA55ED0E6 /* Debug */, 7B4DBB05E401008022849E9B6A9AF714 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 52DD4DD837C8F96EF786BB17F1227792 /* Build configuration list for PBXNativeTarget "RxCocoa" */ = { isa = XCConfigurationList; buildConfigurations = ( CB6CE1072EC77BB7ADBBC0F4F6A24078 /* Debug */, D142C161ECF4956E8ADB991690FCAD39 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 554EA923463767BCA4352E1E6C624685 /* Build configuration list for PBXNativeTarget "Result" */ = { isa = XCConfigurationList; buildConfigurations = ( 9359037A8F209948A0C05BE187F3F89F /* Debug */, EB18C0ABDA9FBD6260148AE14861F47D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 55B1DC46A281606C5A8A2F5953589206 /* Build configuration list for PBXNativeTarget "Then" */ = { isa = XCConfigurationList; buildConfigurations = ( 57A92DFDF0A03B32F5C887C772B20A22 /* Debug */, D8EA526A6238854CEBFF0440AE6D0BEF /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 5A05DD5251EAD538258DF17673557953 /* Build configuration list for PBXNativeTarget "URLNavigator" */ = { isa = XCConfigurationList; buildConfigurations = ( 0667D7072671234E32C33BAE8737DA70 /* Debug */, F277A68C2ED70936C636EC5B204AB50E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 6252E31AF78FAB4E0F676126FABEADB5 /* Build configuration list for PBXNativeTarget "NSObject+Rx" */ = { isa = XCConfigurationList; buildConfigurations = ( 211EA9C8BE0D10435468E86A282A9AD9 /* Debug */, A9B73D0C39ADC5559CCF5B20A1CE4053 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 63A8CDD2A9A34DC6C67006A603063404 /* Build configuration list for PBXNativeTarget "RxAlamofire" */ = { isa = XCConfigurationList; buildConfigurations = ( 29804FEF0B8246EFF6ED65D0F799AEA3 /* Debug */, 78F2DC967C87777534BEEC714E8BD1E0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 697E3CDF0A55702AF195B90A1000CA13 /* Build configuration list for PBXNativeTarget "Differentiator" */ = { isa = XCConfigurationList; buildConfigurations = ( BB895A8B8C5E6F6144E3E40F6BDE4051 /* Debug */, 694AA0C88B645D5A0D978E74AA494B72 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 6B1519F70E09D2830A7F592841F05038 /* Build configuration list for PBXNativeTarget "TYPagerController" */ = { isa = XCConfigurationList; buildConfigurations = ( 2529F6EDCBF2615DE21E47D137D4283B /* Debug */, 536A4065D0055B28FCC2536BA827FBC6 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 704D48507B0C8F42CE3629D21F48652A /* Build configuration list for PBXNativeTarget "SwiftyJSON" */ = { isa = XCConfigurationList; buildConfigurations = ( 34DFB997862DE04F29457821E6B6FD62 /* Debug */, 165696DD5DC4E8A99EB8459726B5F42E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 7CEE893A728FE595E38B19DA5C14A481 /* Build configuration list for PBXNativeTarget "TYCyclePagerView" */ = { isa = XCConfigurationList; buildConfigurations = ( 0A523BF1DE09BDF5626D574CC59B658E /* Debug */, C4DDADE7FC0CC645D75299F75A9ACF65 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 860035FE08E0668CFA26227D241C13AC /* Build configuration list for PBXNativeTarget "RxSwift" */ = { isa = XCConfigurationList; buildConfigurations = ( 8B75AF8EE7468D85BDC95BF5356A6B48 /* Debug */, 6292F718E229B9D83E841718720DD31B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 8C1E4CEFB2390CBE75E70BAC20F44BBF /* Build configuration list for PBXNativeTarget "Kingfisher" */ = { isa = XCConfigurationList; buildConfigurations = ( 93D684927DE728E6F82AB56702FC59F2 /* Debug */, F65473631C6DA6653F95FF57B7FCB48C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 8E2F09332A7598018177CF140EEDFC0E /* Build configuration list for PBXNativeTarget "Moya" */ = { isa = XCConfigurationList; buildConfigurations = ( 3D87FE564C17E455688A363472861A61 /* Debug */, 68875726E361E4D5EE55450A0565AE5E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 90DBC69F7407A38678F533AF966E180A /* Build configuration list for PBXNativeTarget "ReactorKit" */ = { isa = XCConfigurationList; buildConfigurations = ( BAF9A20A5B0B25A6F56D6CF4C427B72F /* Debug */, 0149886A2ED9ED38B90DCC0CB07F5228 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; AD3D5B9E24ECB0D75B3E0294644E66D1 /* Build configuration list for PBXNativeTarget "SnapKit" */ = { isa = XCConfigurationList; buildConfigurations = ( EFD3CA7A881278528426A95F79CD1C07 /* Debug */, 33E464B02591D1161D80223BA28CA6F7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BD5CCA9B364C93A19C3C8B5200B7D56A /* Build configuration list for PBXNativeTarget "RxDataSources" */ = { isa = XCConfigurationList; buildConfigurations = ( A46F53241451B8AA7DF21F81C4C1EE1B /* Debug */, 34F671C601072D988FC80B795FE6B097 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C5214E85BC32F855BB77F4FD74672338 /* Build configuration list for PBXNativeTarget "RxGesture" */ = { isa = XCConfigurationList; buildConfigurations = ( 10081673734687833D9A3EE73A4C28AA /* Debug */, 086A5FDC58C99AFB20FA815843EE41DE /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E8C5CE05A712EF50BB123A41D8EFC0CB /* Build configuration list for PBXNativeTarget "IQKeyboardManagerSwift" */ = { isa = XCConfigurationList; buildConfigurations = ( D88A3909EE1F5CD04C59F0E32C9B8592 /* Debug */, 2E15E9C97438ACF0A953E95909C87920 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; } ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/Alamofire.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/Differentiator.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/IQKeyboardManagerSwift.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/Kingfisher.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/MJRefresh.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/Moya.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/NSObject+Rx.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/ObjectMapper.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/Pods-RxXMLY.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/ReactorKit.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/Result.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/ReusableKit.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/RxAlamofire.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/RxCocoa.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/RxDataSources.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/RxGesture.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/RxSwift.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/SnapKit.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/SwiftyColor.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/SwiftyJSON.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/TTRangeSlider.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/TYCyclePagerView.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/TYPagerController.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/Then.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/URLNavigator.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/sessionCh.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState Alamofire.xcscheme isShown Differentiator.xcscheme isShown IQKeyboardManagerSwift.xcscheme isShown Kingfisher.xcscheme isShown MJRefresh.xcscheme isShown Moya.xcscheme isShown NSObject+Rx.xcscheme isShown ObjectMapper.xcscheme isShown Pods-RxXMLY.xcscheme isShown ReactorKit.xcscheme isShown Result.xcscheme isShown ReusableKit.xcscheme isShown RxAlamofire.xcscheme isShown RxCocoa.xcscheme isShown RxDataSources.xcscheme isShown RxGesture.xcscheme isShown RxSwift.xcscheme isShown SnapKit.xcscheme isShown SwiftyColor.xcscheme isShown SwiftyJSON.xcscheme isShown TTRangeSlider.xcscheme isShown TYCyclePagerView.xcscheme isShown TYPagerController.xcscheme isShown Then.xcscheme isShown URLNavigator.xcscheme isShown SuppressBuildableAutocreation ================================================ FILE: Pods/ReactorKit/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2017 Suyeol Jeon (xoul.kr) 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. ================================================ FILE: Pods/ReactorKit/README.md ================================================ ReactorKit

Swift CocoaPods Platform Build Status Codecov CocoaDocs

ReactorKit is a framework for a reactive and unidirectional Swift application architecture. This repository introduces the basic concept of ReactorKit and describes how to build an application using ReactorKit. You may want to see the [Examples](#examples) section first if you'd like to see the actual code. Visit the [API Reference](http://reactorkit.io/docs/latest/) for code-level documentation. ## Table of Contents * [Basic Concept](#basic-concept) * [Design Goal](#design-goal) * [View](#view) * [Reactor](#reactor) * [Advanced](#advanced) * [Service](#service) * [Global States](#global-states) * [View Communication](#view-communication) * [Testing](#testing) * [Conventions](#conventions) * [Examples](#examples) * [Dependencies](#dependencies) * [Requirements](#requirements) * [Installation](#installation) * [Contributing](#contribution) * [Community](#community) * [Who's using ReactorKit](#whos-using-reactorkit) * [Changelog](#changelog) * [License](#license) ## Basic Concept ReactorKit is a combination of [Flux](https://facebook.github.io/flux/) and [Reactive Programming](https://en.wikipedia.org/wiki/Reactive_programming). The user actions and the view states are delivered to each layer via observable streams. These streams are unidirectional: the view can only emit actions and the reactor can only emit states.

flow

### Design Goal * **Testability**: The first purpose of ReactorKit is to separate the business logic from a view. This can make the code testable. A reactor doesn't have any dependency to a view. Just test reactors and test view bindings. See [Testing](#testing) section for details. * **Start Small**: ReactorKit doesn't require the whole application to follow a single architecture. ReactorKit can be adopted partially, for one or more specific views. You don't need to rewrite everything to use ReactorKit on your existing project. * **Less Typing**: ReactorKit focuses on avoiding complicated code for a simple thing. ReactorKit requires less code compared to other architectures. Start simple and scale up. ### View A *View* displays data. A view controller and a cell are treated as a view. The view binds user inputs to the action stream and binds the view states to each UI component. There's no business logic in a view layer. A view just defines how to map the action stream and the state stream. To define a view, just have an existing class conform a protocol named `View`. Then your class will have a property named `reactor` automatically. This property is typically set outside of the view. ```swift class ProfileViewController: UIViewController, View { var disposeBag = DisposeBag() } profileViewController.reactor = UserViewReactor() // inject reactor ``` When the `reactor` property has changed, `bind(reactor:)` gets called. Implement this method to define the bindings of an action stream and a state stream. ```swift func bind(reactor: ProfileViewReactor) { // action (View -> Reactor) refreshButton.rx.tap.map { Reactor.Action.refresh } .bind(to: reactor.action) .disposed(by: self.disposeBag) // state (Reactor -> View) reactor.state.map { $0.isFollowing } .bind(to: followButton.rx.isSelected) .disposed(by: self.disposeBag) } ``` #### Storyboard Support Use `StoryboardView` protocol if you're using a storyboard to initialize view controllers. Everything is same but the only difference is that the `StoryboardView` performs a binding after the view is loaded. ```swift let viewController = MyViewController() viewController.reactor = MyViewReactor() // will not executes `bind(reactor:)` immediately class MyViewController: UIViewController, StoryboardView { func bind(reactor: MyViewReactor) { // this is called after the view is loaded (viewDidLoad) } } ``` ### Reactor A *Reactor* is an UI-independent layer which manages the state of a view. The foremost role of a reactor is to separate control flow from a view. Every view has its corresponding reactor and delegates all logic to its reactor. A reactor has no dependency to a view, so it can be easily tested. Conform to the `Reactor` protocol to define a reactor. This protocol requires three types to be defined: `Action`, `Mutation` and `State`. It also requires a property named `initialState`. ```swift class ProfileViewReactor: Reactor { // represent user actions enum Action { case refreshFollowingStatus(Int) case follow(Int) } // represent state changes enum Mutation { case setFollowing(Bool) } // represents the current view state struct State { var isFollowing: Bool = false } let initialState: State = State() } ``` An `Action` represents a user interaction and `State` represents a view state. `Mutation` is a bridge between `Action` and `State`. A reactor converts the action stream to the state stream in two steps: `mutate()` and `reduce()`.

flow-reactor

#### `mutate()` `mutate()` receives an `Action` and generates an `Observable`. ```swift func mutate(action: Action) -> Observable ``` Every side effect, such as an async operation or API call, is performed in this method. ```swift func mutate(action: Action) -> Observable { switch action { case let .refreshFollowingStatus(userID): // receive an action return UserAPI.isFollowing(userID) // create an API stream .map { (isFollowing: Bool) -> Mutation in return Mutation.setFollowing(isFollowing) // convert to Mutation stream } case let .follow(userID): return UserAPI.follow() .map { _ -> Mutation in return Mutation.setFollowing(true) } } } ``` #### `reduce()` `reduce()` generates a new `State` from a previous `State` and a `Mutation`. ```swift func reduce(state: State, mutation: Mutation) -> State ``` This method is a pure function. It should just return a new `State` synchronously. Don't perform any side effects in this function. ```swift func reduce(state: State, mutation: Mutation) -> State { var state = state // create a copy of the old state switch mutation { case let .setFollowing(isFollowing): state.isFollowing = isFollowing // manipulate the state, creating a new state return state // return the new state } } ``` #### `transform()` `transform()` transforms each stream. There are three `transform()` functions: ```swift func transform(action: Observable) -> Observable func transform(mutation: Observable) -> Observable func transform(state: Observable) -> Observable ``` Implement these methods to transform and combine with other observable streams. For example, `transform(mutation:)` is the best place for combining a global event stream to a mutation stream. See the [Global States](#global-states) section for details. These methods can be also used for debugging purposes: ```swift func transform(action: Observable) -> Observable { return action.debug("action") // Use RxSwift's debug() operator } ``` ## Advanced ### Service ReactorKit has a special layer named *Service*. A service layer does the actual business logic. A reactor is a middle layer between a view and a service which manages event streams. When a reactor receives an user action from a view, the reactor calls the service logic. The service makes a network request and sends the response back to the reactor. Then the reactor create a mutation stream with the service response. Here is an example of service: ```swift protocol UserServiceType { func user(id: Int) -> Observable func follow(id: Int) -> Observable } final class UserService: Service, UserServiceType { func user(id: Int) -> Observable { return foo() } func follow(id: Int) -> Observable { return bar() } } ``` ### Global States Unlike Redux, ReactorKit doesn't define a global app state. It means that you can use anything to manage a global state. You can use a `Variable`, a `PublishSubject` or even a reactor. ReactorKit doesn't force to have a global state so you can use ReactorKit in a specific feature in your application. There is no global state in the **Action → Mutation → State** flow. You should use `transform(mutation:)` to transform the global state to a mutation. Let's assume that we have a global `Variable` which stores the current authenticated user. If you'd like to emit a `Mutation.setUser(User?)` when the `currentUser` is changed, you can do as following: ```swift var currentUser: Variable // global state func transform(mutation: Observable) -> Observable { return Observable.merge(mutation, currentUser.map(Mutation.setUser)) } ``` Then the mutation will be emitted each time the view sends an action to a reactor and the `currentUser` is changed. ### View Communication You must be familiar with callback closures or delegate patterns for communicating between multiple views. ReactorKit recommends you to use [reactive extensions](https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Reactive.swift) for it. The most common example of `ControlEvent` is `UIButton.rx.tap`. The key concept is to treat your custom views as UIButton or UILabel.

view-view

Let's assume that we have a `ChatViewController` which displays messages. The `ChatViewController` owns a `MessageInputView`. When an user taps the send button on the `MessageInputView`, the text will be sent to the `ChatViewController` and `ChatViewController` will bind in to the reactor's action. This is an example `MessageInputView`'s reactive extension: ```swift extension Reactive where Base: MessageInputView { var sendButtonTap: ControlEvent { let source = base.sendButton.rx.tap.withLatestFrom(...) return ControlEvent(events: source) } } ``` You can use that extension in the `ChatViewController`. For example: ```swift messageInputView.rx.sendButtonTap .map(Reactor.Action.send) .bind(to: reactor.action) ``` ### Testing ReactorKit has a built-in functionality for a testing. You'll be able to easily test both a view and a reactor with a following instruction. #### What to test First of all, you have to decide what to test. There are two things to test: a view and a reactor. * View * Action: is a proper action sent to a reactor with a given user interaction? * State: is a view property set properly with a following state? * Reactor * State: is a state changed properly with an action? #### View testing A view can be tested with a *stub* reactor. A reactor has a property `stub` which can log actions and force change states. If a reactor's stub is enabled, both `mutate()` and `reduce()` are not executed. A stub has these properties: ```swift var isEnabled: Bool { get set } var state: Variable { get } var action: ActionSubject { get } var actions: [Reactor.Action] { get } // recorded actions ``` Here are some example test cases: ```swift func testAction_refresh() { // 1. prepare a stub reactor let reactor = MyReactor() reactor.stub.isEnabled = true // 2. prepare a view with a stub reactor let view = MyView() view.reactor = reactor // 3. send an user interaction programatically view.refreshControl.sendActions(for: .valueChanged) // 4. assert actions XCTAssertEqual(reactor.stub.actions.last, .refresh) } func testState_isLoading() { // 1. prepare a stub reactor let reactor = MyReactor() reactor.stub.isEnabled = true // 2. prepare a view with a stub reactor let view = MyView() view.reactor = reactor // 3. set a stub state reactor.stub.state.value = MyReactor.State(isLoading: true) // 4. assert view properties XCTAssertEqual(view.activityIndicator.isAnimating, true) } ``` #### Reactor testing A reactor can be tested independently. ```swift func testIsBookmarked() { let reactor = MyReactor() reactor.action.onNext(.toggleBookmarked) XCTAssertEqual(reactor.currentState.isBookmarked, true) reactor.action.onNext(.toggleBookmarked) XCTAssertEqual(reactor.currentState.isBookmarked, false) } ``` Sometimes a state is changed more than one time for a single action. For example, a `.refresh` action sets `state.isLoading` to `true` at first and sets to `false` after the refreshing. In this case it's difficult to test `state.isLoading` with `currentState` so you might need to use [RxTest](https://github.com/ReactiveX/RxSwift) or [RxExpect](https://github.com/devxoul/RxExpect). Here is an example test case using RxExpect: ```swift func testIsLoading() { RxExpect("it should change isLoading") { test in let reactor = test.retain(MyReactor()) test.input(reactor.action, [ next(100, .refresh) // send .refresh at 100 scheduler time ]) test.assert(reactor.state.map { $0.isLoading }) .since(100) // values since 100 scheduler time .assert([ true, // just after .refresh false, // after refreshing ]) } } ``` ## Conventions ReactorKit suggests some conventions to write clean and concise code. * You must create a reactor outside of the view and pass it to the view's `reactor` property. **Good** ```swift let view = MyView() view.reactor = MyViewReactor(provider: provider) ``` **Bad** ```swift class MyView: UIView, View { init() { self.reactor = MyViewReactor() } } ``` ## Examples * [Counter](https://github.com/ReactorKit/ReactorKit/tree/master/Examples/Counter): The most simple and basic example of ReactorKit * [GitHub Search](https://github.com/ReactorKit/ReactorKit/tree/master/Examples/GitHubSearch): A simple application which provides a GitHub repository search * [RxTodo](https://github.com/devxoul/RxTodo): iOS Todo Application using ReactorKit * [Cleverbot](https://github.com/devxoul/Cleverbot): iOS Messaging Application using Cleverbot and ReactorKit * [Drrrible](https://github.com/devxoul/Drrrible): Dribbble for iOS using ReactorKit ([App Store](https://itunes.apple.com/us/app/drrrible/id1229592223?mt=8)) * [Passcode](https://github.com/cruisediary/Passcode): Passcode for iOS RxSwift, ReactorKit and IGListKit example ## Dependencies * [RxSwift](https://github.com/ReactiveX/RxSwift) >= 4.0 ## Requirements * Swift 4 * iOS 8 * macOS 10.11 * tvOS 9.0 * watchOS 2.0 ## Installation * **Using [CocoaPods](https://cocoapods.org)**: ```ruby pod 'ReactorKit' ``` * **Using [Carthage](https://github.com/Carthage/Carthage)**: ``` github "ReactorKit/ReactorKit" ``` ## Contribution Any discussions and pull requests are welcomed 💖 * To development: ```console $ TEST=1 swift package generate-xcodeproj ``` * To test: ```console $ swift test ``` ## Community Join [#reactorkit](https://rxswift.slack.com/messages/C561PETRN/) on [RxSwift Slack](http://rxswift-slack.herokuapp.com/)! ## Who's using ReactorKit

StyleShare Kakao DocTalk

> Are you using ReactorKit? Please [let me know](mailto:devxoul+reactorkit@gmail.com)! ## Changelog * 2017-04-18 * Change the repository name to ReactorKit. * 2017-03-17 * Change the architecture name from RxMVVM to The Reactive Architecture. * Every ViewModels are renamed to ViewReactors. ## License ReactorKit is under MIT license. See the [LICENSE](https://github.com/ReactorKit/ReactorKit/blob/master/LICENSE) for more info. ================================================ FILE: Pods/ReactorKit/Sources/ReactorKit/ActionSubject.swift ================================================ // // ActionSubject.swift // ReactorKit // // Created by Suyeol Jeon on 14/05/2017. // // import class Foundation.NSLock.NSRecursiveLock import RxSwift /// A special subject for Reactor's Action. It only emits `.next` event. public final class ActionSubject: ObservableType, ObserverType, SubjectType { public typealias E = Element typealias Key = UInt var lock = NSRecursiveLock() var nextKey: Key = 0 var observers: [Key: (Event) -> ()] = [:] #if TRACE_RESOURCES init() { _ = Resources.incrementTotal() } #endif #if TRACE_RESOURCES deinit { _ = Resources.decrementTotal() } #endif public func subscribe(_ observer: O) -> Disposable where O.E == Element { self.lock.lock() let key = self.nextKey self.nextKey += 1 self.observers[key] = observer.on self.lock.unlock() return Disposables.create { [weak self] in self?.lock.lock() self?.observers.removeValue(forKey: key) self?.lock.unlock() } } public func on(_ event: Event) { self.lock.lock() if case .next = event { self.observers.values.forEach { $0(event) } } self.lock.unlock() } } ================================================ FILE: Pods/ReactorKit/Sources/ReactorKit/AssociatedObjectStore.swift ================================================ // // AssociatedObjectStore.swift // ReactorKit // // Created by Suyeol Jeon on 14/04/2017. // Copyright © 2017 Suyeol Jeon. All rights reserved. // import ObjectiveC public protocol AssociatedObjectStore { } extension AssociatedObjectStore { func associatedObject(forKey key: UnsafeRawPointer) -> T? { return objc_getAssociatedObject(self, key) as? T } func associatedObject(forKey key: UnsafeRawPointer, default: @autoclosure () -> T) -> T { if let object: T = self.associatedObject(forKey: key) { return object } let object = `default`() self.setAssociatedObject(object, forKey: key) return object } func setAssociatedObject(_ object: T?, forKey key: UnsafeRawPointer) { objc_setAssociatedObject(self, key, object, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } ================================================ FILE: Pods/ReactorKit/Sources/ReactorKit/Reactor.swift ================================================ // // Reactor.swift // ReactorKit // // Created by Suyeol Jeon on 06/04/2017. // Copyright © 2017 Suyeol Jeon. All rights reserved. // import RxSwift public struct NoAction {} public struct NoMutation {} public typealias _Reactor = Reactor /// A Reactor is an UI-independent layer which manages the state of a view. The foremost role of a /// reactor is to separate control flow from a view. Every view has its corresponding reactor and /// delegates all logic to its reactor. A reactor has no dependency to a view, so it can be easily /// tested. public protocol Reactor: class, AssociatedObjectStore { /// An action represents user actions. associatedtype Action /// A mutation represents state changes. associatedtype Mutation = Action /// A State represents the current state of a view. associatedtype State /// The action from the view. Bind user inputs to this subject. var action: ActionSubject { get } /// The initial state. var initialState: State { get } /// The current state. This value is changed just after the state stream emits a new state. var currentState: State { get } /// The state stream. Use this observable to observe the state changes. var state: Observable { get } /// Transforms the action. Use this function to combine with other observables. This method is /// called once before the state stream is created. func transform(action: Observable) -> Observable /// Commits mutation from the action. This is the best place to perform side-effects such as /// async tasks. func mutate(action: Action) -> Observable /// Transforms the mutation stream. Implement this method to transform or combine with other /// observables. This method is called once before the state stream is created. func transform(mutation: Observable) -> Observable /// Generates a new state with the previous state and the action. It should be purely functional /// so it should not perform any side-effects here. This method is called every time when the /// mutation is committed. func reduce(state: State, mutation: Mutation) -> State /// Transforms the state stream. Use this function to perform side-effects such as logging. This /// method is called once after the state stream is created. func transform(state: Observable) -> Observable } // MARK: - Associated Object Keys private var actionKey = "action" private var currentStateKey = "currentState" private var stateKey = "state" private var disposeBagKey = "disposeBag" private var stubKey = "stub" // MARK: - Default Implementations extension Reactor { private var _action: ActionSubject { if self.stub.isEnabled { return self.stub.action } else { return self.associatedObject(forKey: &actionKey, default: .init()) } } public var action: ActionSubject { // Creates a state stream automatically _ = self._state // It seems that Swift has a bug in associated object when subclassing a generic class. This is // a temporary solution to bypass the bug. See #30 for details. return self._action } public internal(set) var currentState: State { get { return self.associatedObject(forKey: ¤tStateKey, default: self.initialState) } set { self.setAssociatedObject(newValue, forKey: ¤tStateKey) } } private var _state: Observable { if self.stub.isEnabled { return self.stub.state.asObservable() } else { return self.associatedObject(forKey: &stateKey, default: self.createStateStream()) } } public var state: Observable { // It seems that Swift has a bug in associated object when subclassing a generic class. This is // a temporary solution to bypass the bug. See #30 for details. return self._state } fileprivate var disposeBag: DisposeBag { get { return self.associatedObject(forKey: &disposeBagKey, default: DisposeBag()) } } public func createStateStream() -> Observable { let action = self._action.asObservable() let transformedAction = self.transform(action: action) let mutation = transformedAction .flatMap { [weak self] action -> Observable in guard let `self` = self else { return .empty() } return self.mutate(action: action).catchError { _ in .empty() } } let transformedMutation = self.transform(mutation: mutation) let state = transformedMutation .scan(self.initialState) { [weak self] state, mutation -> State in guard let `self` = self else { return state } return self.reduce(state: state, mutation: mutation) } .catchError { _ in .empty() } .startWith(self.initialState) .observeOn(MainScheduler.instance) let transformedState = self.transform(state: state) .do(onNext: { [weak self] state in self?.currentState = state }) .replay(1) transformedState.connect().disposed(by: self.disposeBag) return transformedState } public func transform(action: Observable) -> Observable { return action } public func mutate(action: Action) -> Observable { return .empty() } public func transform(mutation: Observable) -> Observable { return mutation } public func reduce(state: State, mutation: Mutation) -> State { return state } public func transform(state: Observable) -> Observable { return state } } extension Reactor where Action == Mutation { public func mutate(action: Action) -> Observable { return .just(action) } } // MARK: - Stub extension Reactor { public var stub: Stub { return self.associatedObject( forKey: &stubKey, default: .init(reactor: self, disposeBag: self.disposeBag) ) } } ================================================ FILE: Pods/ReactorKit/Sources/ReactorKit/StoryboardView.swift ================================================ import RxSwift #if !os(Linux) #if os(iOS) || os(tvOS) import UIKit private typealias OSViewController = UIViewController #elseif os(OSX) import AppKit private typealias OSViewController = NSViewController #endif public protocol _ObjCStoryboardView { func performBinding() } public protocol StoryboardView: View, _ObjCStoryboardView { } extension StoryboardView { public var reactor: Reactor? { get { return self.associatedObject(forKey: &reactorKey) } set { self.setAssociatedObject(newValue, forKey: &reactorKey) self.isReactorBinded = false self.disposeBag = DisposeBag() self.performBinding() } } fileprivate var isReactorBinded: Bool { get { return self.associatedObject(forKey: &isReactorBindedKey, default: false) } set { self.setAssociatedObject(newValue, forKey: &isReactorBindedKey) } } public func performBinding() { guard let reactor = self.reactor else { return } guard !self.isReactorBinded else { return } guard !self.shouldDeferBinding(reactor: reactor) else { return } self.bind(reactor: reactor) self.isReactorBinded = true } fileprivate func shouldDeferBinding(reactor: Reactor) -> Bool { #if !os(watchOS) return (self as? OSViewController)?.isViewLoaded == false #else return false #endif } } #if !os(watchOS) extension OSViewController { @objc func _reactorkit_performBinding() { (self as? _ObjCStoryboardView)?.performBinding() } } #endif #endif ================================================ FILE: Pods/ReactorKit/Sources/ReactorKit/Stub.swift ================================================ import RxSwift public class Stub { private unowned var reactor: Reactor private let disposeBag: DisposeBag public var isEnabled: Bool = false public let state: Variable public let action: ActionSubject public private(set) var actions: [Reactor.Action] = [] public init(reactor: Reactor, disposeBag: DisposeBag) { self.reactor = reactor self.disposeBag = disposeBag self.state = .init(reactor.initialState) self.state.asObservable() .subscribe(onNext: { [weak reactor] state in reactor?.currentState = state }) .disposed(by: disposeBag) self.action = .init() self.action .subscribe(onNext: { [weak self] action in self?.actions.append(action) }) .disposed(by: self.disposeBag) } } ================================================ FILE: Pods/ReactorKit/Sources/ReactorKit/View.swift ================================================ // // View.swift // ReactorKit // // Created by Suyeol Jeon on 13/04/2017. // Copyright © 2017 Suyeol Jeon. All rights reserved. // #if !os(Linux) import RxSwift /// A View displays data. A view controller and a cell are treated as a view. The view binds user /// inputs to the action stream and binds the view states to each UI component. There's no business /// logic in a view layer. A view just defines how to map the action stream and the state stream. public protocol View: class, AssociatedObjectStore { associatedtype Reactor: _Reactor /// A dispose bag. It is disposed each time the `reactor` is assigned. var disposeBag: DisposeBag { get set } /// A view's reactor. `bind(reactor:)` gets called when the new value is assigned to this property. var reactor: Reactor? { get set } /// Creates RxSwift bindings. This method is called each time the `reactor` is assigned. /// /// Here is a typical implementation example: /// /// ``` /// func bind(reactor: MyReactor) { /// // Action /// increaseButton.rx.tap /// .bind(to: Reactor.Action.increase) /// .disposed(by: disposeBag) /// /// // State /// reactor.state.map { $0.count } /// .bind(to: countLabel.rx.text) /// .disposed(by: disposeBag) /// } /// ``` /// /// - warning: It's not recommended to call this method directly. func bind(reactor: Reactor) } // MARK: - Associated Object Keys var reactorKey = "reactor" var isReactorBindedKey = "isReactorBinded" // MARK: - Default Implementations extension View { public var reactor: Reactor? { get { return self.associatedObject(forKey: &reactorKey) } set { self.setAssociatedObject(newValue, forKey: &reactorKey) self.disposeBag = DisposeBag() if let reactor = newValue { self.bind(reactor: reactor) } } } } #endif ================================================ FILE: Pods/ReactorKit/Sources/ReactorKitRuntime/ReactorKitRuntime.m ================================================ #import #import "ReactorKitRuntime.h" @implementation NSObject (ReactorKit) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self swizzleInitializeOfClassNamed:@"UIViewController"]; [self swizzleInitializeOfClassNamed:@"NSViewController"]; }); } + (void)swizzleInitializeOfClassNamed:(NSString *)className { Class class = NSClassFromString(className); if (!class) { return; } method_exchangeImplementations(class_getClassMethod(class, @selector(initialize)), class_getClassMethod(self, @selector(_reactorkit_initialize))); } + (void)_reactorkit_initialize { [self _reactorkit_initialize]; BOOL isUIViewController = [self isSubclassOfClassNamed:@"UIViewController"]; BOOL isNSViewController = [self isSubclassOfClassNamed:@"NSViewController"]; if (!isUIViewController && !isNSViewController) { return; } [self swizzleViewDidLoad]; } + (void)swizzleViewDidLoad { Class class = self; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" SEL oldSelector = @selector(viewDidLoad); SEL performBindingSelector = @selector(_reactorkit_performBinding); #pragma clang diagnostic pop Method oldMethod = class_getInstanceMethod(class, oldSelector); const char *types = method_getTypeEncoding(oldMethod); void (*oldMethodImp)(id, SEL) = (void (*)(id, SEL))method_getImplementation(oldMethod); IMP newMethodImp = imp_implementationWithBlock(^(__unsafe_unretained id self) { oldMethodImp(self, oldSelector); if ([self respondsToSelector:performBindingSelector]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector:performBindingSelector]; #pragma clang diagnostic pop } }); class_replaceMethod(class, oldSelector, newMethodImp, types); } + (BOOL)isSubclassOfClassNamed:(NSString *)className { Class superclass = NSClassFromString(className); if (!superclass) { return NO; } return [self isSubclassOfClass:superclass]; } @end ================================================ FILE: Pods/ReactorKit/Sources/ReactorKitRuntime/include/ReactorKitRuntime.h ================================================ #import //! Project version number for ReactorKit. FOUNDATION_EXPORT double ReactorKitVersionNumber; //! Project version string for ReactorKit. FOUNDATION_EXPORT const unsigned char ReactorKitVersionString[]; ================================================ FILE: Pods/Result/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Rob Rix 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. ================================================ FILE: Pods/Result/README.md ================================================ # Result [![Build Status](https://travis-ci.org/antitypical/Result.svg?branch=master)](https://travis-ci.org/antitypical/Result) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods](https://img.shields.io/cocoapods/v/Result.svg)](https://cocoapods.org/) [![Reference Status](https://www.versioneye.com/objective-c/result/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/result/references) This is a Swift µframework providing `Result`. `Result` values are either successful (wrapping `Value`) or failed (wrapping `Error`). This is similar to Swift’s native `Optional` type: `success` is like `some`, and `failure` is like `none` except with an associated `Error` value. The addition of an associated `Error` allows errors to be passed along for logging or displaying to the user. Using this µframework instead of rolling your own `Result` type allows you to easily interface with other frameworks that also use `Result`. ## Use Use `Result` whenever an operation has the possibility of failure. Consider the following example of a function that tries to extract a `String` for a given key from a JSON `Dictionary`. ```swift typealias JSONObject = [String: Any] enum JSONError: Error { case noSuchKey(String) case typeMismatch } func stringForKey(json: JSONObject, key: String) -> Result { guard let value = json[key] else { return .failure(.noSuchKey(key)) } if let value = value as? String { return .success(value) } else { return .failure(.typeMismatch) } } ``` This function provides a more robust wrapper around the default subscripting provided by `Dictionary`. Rather than return `Any?`, it returns a `Result` that either contains the `String` value for the given key, or an `ErrorType` detailing what went wrong. One simple way to handle a `Result` is to deconstruct it using a `switch` statement. ```swift switch stringForKey(json, key: "email") { case let .success(email): print("The email is \(email)") case let .failure(.noSuchKey(key)): print("\(key) is not a valid key") case .failure(.typeMismatch): print("Didn't have the right type") } ``` Using a `switch` statement allows powerful pattern matching, and ensures all possible results are covered. Swift 2.0 offers new ways to deconstruct enums like the `if-case` statement, but be wary as such methods do not ensure errors are handled. Other methods available for processing `Result` are detailed in the [API documentation](http://cocoadocs.org/docsets/Result/). ## Result vs. Throws Swift 2.0 introduces error handling via throwing and catching `Error`. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction enables powerful functionality such as `map` and `flatMap`, making `Result` more composable than `throw`. Since dealing with APIs that throw is common, you can convert such functions into a `Result` by using the `materialize` method. Conversely, a `Result` can be used to throw an error by calling `dematerialize`. ## Higher Order Functions `map` and `flatMap` operate the same as `Optional.map` and `Optional.flatMap` except they apply to `Result`. `map` transforms a `Result` into a `Result` of a new type. It does this by taking a function that transforms the `Value` type into a new value. This transformation is only applied in the case of a `success`. In the case of a `failure`, the associated error is re-wrapped in the new `Result`. ```swift // transforms a Result to a Result let idResult = intForKey(json, key:"id").map { id in String(id) } ``` Here, the final result is either the id as a `String`, or carries over the `failure` from the previous result. `flatMap` is similar to `map` in that in transforms the `Result` into another `Result`. However, the function passed into `flatMap` must return a `Result`. An in depth discussion of `map` and `flatMap` is beyond the scope of this documentation. If you would like a deeper understanding, read about functors and monads. This article is a good place to [start](http://www.javiersoto.me/post/106875422394). ## Integration ### Carthage 1. Add this repository as a submodule and/or [add it to your Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile) if you’re using [carthage](https://github.com/Carthage/Carthage/) to manage your dependencies. 2. Drag `Result.xcodeproj` into your project or workspace. 3. Link your target against `Result.framework`. 4. Application targets should ensure that the framework gets copied into their application bundle. (Framework targets should instead require the application linking them to include Result.) ### Cocoapods ```ruby pod 'Result', '~> 3.0.0' ``` ### Swift Package Manager ```swift import PackageDescription let package = Package( name: "MyProject", targets: [], dependencies: [ .Package(url: "https://github.com/antitypical/Result.git", majorVersion: 3) ] ) ``` ================================================ FILE: Pods/Result/Result/Result.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. /// An enum representing either a failure with an explanatory error, or a success with a result value. public enum Result: ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible { case success(T) case failure(Error) // MARK: Constructors /// Constructs a success wrapping a `value`. public init(value: T) { self = .success(value) } /// Constructs a failure wrapping an `error`. public init(error: Error) { self = .failure(error) } /// Constructs a result from an `Optional`, failing with `Error` if `nil`. public init(_ value: T?, failWith: @autoclosure () -> Error) { self = value.map(Result.success) ?? .failure(failWith()) } /// Constructs a result from a function that uses `throw`, failing with `Error` if throws. public init(_ f: @autoclosure () throws -> T) { self.init(attempt: f) } /// Constructs a result from a function that uses `throw`, failing with `Error` if throws. public init(attempt f: () throws -> T) { do { self = .success(try f()) } catch var error { if Error.self == AnyError.self { error = AnyError(error) } self = .failure(error as! Error) } } // MARK: Deconstruction /// Returns the value from `success` Results or `throw`s the error. public func dematerialize() throws -> T { switch self { case let .success(value): return value case let .failure(error): throw error } } /// Case analysis for Result. /// /// Returns the value produced by applying `ifFailure` to `failure` Results, or `ifSuccess` to `success` Results. public func analysis(ifSuccess: (T) -> Result, ifFailure: (Error) -> Result) -> Result { switch self { case let .success(value): return ifSuccess(value) case let .failure(value): return ifFailure(value) } } // MARK: Errors /// The domain for errors constructed by Result. public static var errorDomain: String { return "com.antitypical.Result" } /// The userInfo key for source functions in errors constructed by Result. public static var functionKey: String { return "\(errorDomain).function" } /// The userInfo key for source file paths in errors constructed by Result. public static var fileKey: String { return "\(errorDomain).file" } /// The userInfo key for source file line numbers in errors constructed by Result. public static var lineKey: String { return "\(errorDomain).line" } /// Constructs an error. public static func error(_ message: String? = nil, function: String = #function, file: String = #file, line: Int = #line) -> NSError { var userInfo: [String: Any] = [ functionKey: function, fileKey: file, lineKey: line, ] if let message = message { userInfo[NSLocalizedDescriptionKey] = message } return NSError(domain: errorDomain, code: 0, userInfo: userInfo) } // MARK: CustomStringConvertible public var description: String { return analysis( ifSuccess: { ".success(\($0))" }, ifFailure: { ".failure(\($0))" }) } // MARK: CustomDebugStringConvertible public var debugDescription: String { return description } } // MARK: - Derive result from failable closure public func materialize(_ f: () throws -> T) -> Result { return materialize(try f()) } public func materialize(_ f: @autoclosure () throws -> T) -> Result { do { return .success(try f()) } catch { return .failure(AnyError(error)) } } @available(*, deprecated, message: "Use the overload which returns `Result` instead") public func materialize(_ f: () throws -> T) -> Result { return materialize(try f()) } @available(*, deprecated, message: "Use the overload which returns `Result` instead") public func materialize(_ f: @autoclosure () throws -> T) -> Result { do { return .success(try f()) } catch { // This isn't great, but it lets us maintain compatibility until this deprecated // method can be removed. #if _runtime(_ObjC) return .failure(error as NSError) #else // https://github.com/apple/swift-corelibs-foundation/blob/swift-3.0.2-RELEASE/Foundation/NSError.swift#L314 let userInfo = _swift_Foundation_getErrorDefaultUserInfo(error) as? [String: Any] let nsError = NSError(domain: error._domain, code: error._code, userInfo: userInfo) return .failure(nsError) #endif } } // MARK: - Cocoa API conveniences #if !os(Linux) /// Constructs a `Result` with the result of calling `try` with an error pointer. /// /// This is convenient for wrapping Cocoa API which returns an object or `nil` + an error, by reference. e.g.: /// /// Result.try { NSData(contentsOfURL: URL, options: .dataReadingMapped, error: $0) } @available(*, deprecated, message: "This will be removed in Result 4.0. Use `Result.init(attempt:)` instead. See https://github.com/antitypical/Result/issues/85 for the details.") public func `try`(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> T?) -> Result { var error: NSError? return `try`(&error).map(Result.success) ?? .failure(error ?? Result.error(function: function, file: file, line: line)) } /// Constructs a `Result` with the result of calling `try` with an error pointer. /// /// This is convenient for wrapping Cocoa API which returns a `Bool` + an error, by reference. e.g.: /// /// Result.try { NSFileManager.defaultManager().removeItemAtURL(URL, error: $0) } @available(*, deprecated, message: "This will be removed in Result 4.0. Use `Result.init(attempt:)` instead. See https://github.com/antitypical/Result/issues/85 for the details.") public func `try`(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> Bool) -> Result<(), NSError> { var error: NSError? return `try`(&error) ? .success(()) : .failure(error ?? Result<(), NSError>.error(function: function, file: file, line: line)) } #endif // MARK: - ErrorConvertible conformance extension NSError: ErrorConvertible { public static func error(from error: Swift.Error) -> Self { func cast(_ error: Swift.Error) -> T { return error as! T } return cast(error) } } // MARK: - Errors /// An “error” that is impossible to construct. /// /// This can be used to describe `Result`s where failures will never /// be generated. For example, `Result` describes a result that /// contains an `Int`eger and is guaranteed never to be a `failure`. public enum NoError: Swift.Error, Equatable { public static func ==(lhs: NoError, rhs: NoError) -> Bool { return true } } /// A type-erased error which wraps an arbitrary error instance. This should be /// useful for generic contexts. public struct AnyError: Swift.Error { /// The underlying error. public let error: Swift.Error public init(_ error: Swift.Error) { if let anyError = error as? AnyError { self = anyError } else { self.error = error } } } extension AnyError: ErrorConvertible { public static func error(from error: Error) -> AnyError { return AnyError(error) } } extension AnyError: CustomStringConvertible { public var description: String { return String(describing: error) } } // There appears to be a bug in Foundation on Linux which prevents this from working: // https://bugs.swift.org/browse/SR-3565 // Don't forget to comment the tests back in when removing this check when it's fixed! #if !os(Linux) extension AnyError: LocalizedError { public var errorDescription: String? { return error.localizedDescription } public var failureReason: String? { return (error as? LocalizedError)?.failureReason } public var helpAnchor: String? { return (error as? LocalizedError)?.helpAnchor } public var recoverySuggestion: String? { return (error as? LocalizedError)?.recoverySuggestion } } #endif // MARK: - migration support extension Result { @available(*, unavailable, renamed: "success") public static func Success(_: T) -> Result { fatalError() } @available(*, unavailable, renamed: "failure") public static func Failure(_: Error) -> Result { fatalError() } } extension NSError { @available(*, unavailable, renamed: "error(from:)") public static func errorFromErrorType(_ error: Swift.Error) -> Self { fatalError() } } import Foundation ================================================ FILE: Pods/Result/Result/ResultProtocol.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. /// A type that can represent either failure with an error or success with a result value. public protocol ResultProtocol { associatedtype Value associatedtype Error: Swift.Error /// Constructs a successful result wrapping a `value`. init(value: Value) /// Constructs a failed result wrapping an `error`. init(error: Error) /// Case analysis for ResultProtocol. /// /// Returns the value produced by appliying `ifFailure` to the error if self represents a failure, or `ifSuccess` to the result value if self represents a success. func analysis(ifSuccess: (Value) -> U, ifFailure: (Error) -> U) -> U /// Returns the value if self represents a success, `nil` otherwise. /// /// A default implementation is provided by a protocol extension. Conforming types may specialize it. var value: Value? { get } /// Returns the error if self represents a failure, `nil` otherwise. /// /// A default implementation is provided by a protocol extension. Conforming types may specialize it. var error: Error? { get } } public extension ResultProtocol { /// Returns the value if self represents a success, `nil` otherwise. public var value: Value? { return analysis(ifSuccess: { $0 }, ifFailure: { _ in nil }) } /// Returns the error if self represents a failure, `nil` otherwise. public var error: Error? { return analysis(ifSuccess: { _ in nil }, ifFailure: { $0 }) } /// Returns a new Result by mapping `Success`es’ values using `transform`, or re-wrapping `Failure`s’ errors. public func map(_ transform: (Value) -> U) -> Result { return flatMap { .success(transform($0)) } } /// Returns the result of applying `transform` to `Success`es’ values, or re-wrapping `Failure`’s errors. public func flatMap(_ transform: (Value) -> Result) -> Result { return analysis( ifSuccess: transform, ifFailure: Result.failure) } /// Returns a Result with a tuple of the receiver and `other` values if both /// are `Success`es, or re-wrapping the error of the earlier `Failure`. public func fanout(_ other: @autoclosure () -> R) -> Result<(Value, R.Value), Error> where Error == R.Error { return self.flatMap { left in other().map { right in (left, right) } } } /// Returns a new Result by mapping `Failure`'s values using `transform`, or re-wrapping `Success`es’ values. public func mapError(_ transform: (Error) -> Error2) -> Result { return flatMapError { .failure(transform($0)) } } /// Returns the result of applying `transform` to `Failure`’s errors, or re-wrapping `Success`es’ values. public func flatMapError(_ transform: (Error) -> Result) -> Result { return analysis( ifSuccess: Result.success, ifFailure: transform) } /// Returns a new Result by mapping `Success`es’ values using `success`, and by mapping `Failure`'s values using `failure`. public func bimap(success: (Value) -> U, failure: (Error) -> Error2) -> Result { return analysis( ifSuccess: { .success(success($0)) }, ifFailure: { .failure(failure($0)) } ) } } public extension ResultProtocol { // MARK: Higher-order functions /// Returns `self.value` if this result is a .Success, or the given value otherwise. Equivalent with `??` public func recover(_ value: @autoclosure () -> Value) -> Value { return self.value ?? value() } /// Returns this result if it is a .Success, or the given result otherwise. Equivalent with `??` public func recover(with result: @autoclosure () -> Self) -> Self { return analysis( ifSuccess: { _ in self }, ifFailure: { _ in result() }) } } /// Protocol used to constrain `tryMap` to `Result`s with compatible `Error`s. public protocol ErrorConvertible: Swift.Error { static func error(from error: Swift.Error) -> Self } public extension ResultProtocol where Error: ErrorConvertible { /// Returns the result of applying `transform` to `Success`es’ values, or wrapping thrown errors. public func tryMap(_ transform: (Value) throws -> U) -> Result { return flatMap { value in do { return .success(try transform(value)) } catch { let convertedError = Error.error(from: error) // Revisit this in a future version of Swift. https://twitter.com/jckarter/status/672931114944696321 return .failure(convertedError) } } } } // MARK: - Operators infix operator &&& : LogicalConjunctionPrecedence /// Returns a Result with a tuple of `left` and `right` values if both are `Success`es, or re-wrapping the error of the earlier `Failure`. @available(*, deprecated, renamed: "ResultProtocol.fanout(self:_:)") public func &&& (left: L, right: @autoclosure () -> R) -> Result<(L.Value, R.Value), L.Error> where L.Error == R.Error { return left.fanout(right) } precedencegroup ChainingPrecedence { associativity: left higherThan: TernaryPrecedence } infix operator >>- : ChainingPrecedence /// Returns the result of applying `transform` to `Success`es’ values, or re-wrapping `Failure`’s errors. /// /// This is a synonym for `flatMap`. @available(*, deprecated, renamed: "ResultProtocol.flatMap(self:_:)") public func >>- (result: T, transform: (T.Value) -> Result) -> Result { return result.flatMap(transform) } /// Returns `true` if `left` and `right` are both `Success`es and their values are equal, or if `left` and `right` are both `Failure`s and their errors are equal. public func == (left: T, right: T) -> Bool where T.Value: Equatable, T.Error: Equatable { if let left = left.value, let right = right.value { return left == right } else if let left = left.error, let right = right.error { return left == right } return false } /// Returns `true` if `left` and `right` represent different cases, or if they represent the same case but different values. public func != (left: T, right: T) -> Bool where T.Value: Equatable, T.Error: Equatable { return !(left == right) } /// Returns the value of `left` if it is a `Success`, or `right` otherwise. Short-circuits. public func ?? (left: T, right: @autoclosure () -> T.Value) -> T.Value { return left.recover(right()) } /// Returns `left` if it is a `Success`es, or `right` otherwise. Short-circuits. public func ?? (left: T, right: @autoclosure () -> T) -> T { return left.recover(with: right()) } // MARK: - migration support @available(*, unavailable, renamed: "ResultProtocol") public typealias ResultType = ResultProtocol @available(*, unavailable, renamed: "Error") public typealias ResultErrorType = Swift.Error @available(*, unavailable, renamed: "ErrorConvertible") public typealias ErrorTypeConvertible = ErrorConvertible @available(*, deprecated, renamed: "ErrorConvertible") public protocol ErrorProtocolConvertible: ErrorConvertible {} extension ResultProtocol { @available(*, unavailable, renamed: "recover(with:)") public func recoverWith(_ result: @autoclosure () -> Self) -> Self { fatalError() } } extension ErrorConvertible { @available(*, unavailable, renamed: "error(from:)") public static func errorFromErrorType(_ error: Swift.Error) -> Self { fatalError() } } ================================================ FILE: Pods/ReusableKit/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Suyeol Jeon (xoul.kr) 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. ================================================ FILE: Pods/ReusableKit/README.md ================================================ # ReusableKit ![Swift](https://img.shields.io/badge/Swift-3.1-orange.svg) [![CocoaPods](http://img.shields.io/cocoapods/v/ReusableKit.svg)](https://cocoapods.org/pods/ReusableKit) [![Build Status](https://travis-ci.org/devxoul/ReusableKit.svg)](https://travis-ci.org/devxoul/ReusableKit) [![Codecov](https://img.shields.io/codecov/c/github/devxoul/ReusableKit.svg)](https://codecov.io/gh/devxoul/ReusableKit) Generic reusables for Cocoa. Currently supports `UITableView` and `UICollectionView`. ## At a Glance #### Before 🤢 ```swift collectionView.register(UserCell.self, forCellWithReuseIdentifier: "userCell") collectionView.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! UserCell ``` 1. A hard-coded string identifier can cause a human error. 2. A force downcasting should be avoided. #### After 😊 ```swift let reusableUserCell = ReusableCell() collectionView.register(reusableUserCell) collectionView.dequeue(reusableUserCell) // UserCell ``` 1. A string identifier is generated automatically using UUID and stored in the struct. 2. A generic can ensure the type of the dequeued cell statically. ## Example Usage It is recommended to define reusable types as a static constants in an `enum` or a `struct`. #### UITableView ```swift // 1. define enum Reusable { static let headerView = ReusableCell() static let userCell = ReusableCell() } // 2. register tableView.register(Reusable.headerView) tableView.register(Reusable.userCell) // 3. dequeue tableView.dequeue(Reusable.headerView, for: indexPath) tableView.dequeue(Reusable.userCell, for: indexPath) ``` #### UICollectionView ```swift // 1. define enum Reusable { static let headerView = ReusableCell() static let photoCell = ReusableCell() } // 2. register collection.register(Reusable.headerView, kind: .header) collection.register(Reusable.photoCell) // 3. dequeue collection.dequeue(Reusable.headerView, kind: .header, for: indexPath) collection.dequeue(Reusable.photoCell, for: indexPath) ``` #### RxSwift Extension ReusableKit supports a RxSwift extension. ```swift users // Observable<[String]> .bind(to: collectionView.rx.items(Reusable.userCell)) { i, user, cell in cell.user = user } ``` ## Contrubiting Pull requests are welcomed 💖 In order to create Xcode project, run: ```console $ swift package generate-xcodeproj ``` ## Installation - **For iOS 8+ projects** with [CocoaPods](https://cocoapods.org): ```ruby pod 'ReusableKit' pod 'ReusableKit/RxSwift' # with RxSwift extension ``` ## License **ReusableKit** is under MIT license. See the [LICENSE](LICENSE) file for more info. ================================================ FILE: Pods/ReusableKit/Sources/ReusableKit/ReusableKit.swift ================================================ #if os(iOS) import UIKit public protocol CellType: class { var reuseIdentifier: String? { get } } /// A generic class that represents reusable cells. public struct ReusableCell { public typealias Class = Cell public let `class`: Class.Type = Class.self public let identifier: String public let nib: UINib? /// Create and returns a new `ReusableCell` instance. /// /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. /// - parameter nib: A `UINib` instance. Use this when registering from xib. public init(identifier: String? = nil, nib: UINib? = nil) { self.identifier = nib?.instantiate(withOwner: nil, options: nil).lazy .flatMap { ($0 as? CellType)?.reuseIdentifier } .first ?? identifier ?? UUID().uuidString self.nib = nib } /// A convenience initializer. /// /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. /// - parameter nibName: A name of nib. public init(identifier: String? = nil, nibName: String) { let nib = UINib(nibName: nibName, bundle: nil) self.init(identifier: identifier, nib: nib) } } public protocol ViewType: class { } /// A generic class that represents reusable views. public struct ReusableView { public typealias Class = View public let `class`: Class.Type = Class.self public let identifier: String public let nib: UINib? /// Create and returns a new `ReusableView` instance. /// /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. /// - parameter nib: A `UINib` instance. Use this when registering from xib. public init(identifier: String? = nil, nib: UINib? = nil) { self.identifier = identifier ?? UUID().uuidString self.nib = nib } /// A convenience initializer. /// /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. /// - parameter nibName: A name of nib. public init(identifier: String? = nil, nibName: String) { let nib = UINib(nibName: nibName, bundle: nil) self.init(identifier: identifier, nib: nib) } } #endif ================================================ FILE: Pods/ReusableKit/Sources/ReusableKit/UICollectionView+ReusableKit.swift ================================================ #if os(iOS) import UIKit /// An enumeration that represents UICollectionView supplementary view kind. public enum SupplementaryViewKind: String { case header, footer public init?(rawValue: String) { switch rawValue { case UICollectionElementKindSectionHeader: self = .header case UICollectionElementKindSectionFooter: self = .footer default: return nil } } public var rawValue: String { switch self { case .header: return UICollectionElementKindSectionHeader case .footer: return UICollectionElementKindSectionFooter } } } extension UICollectionViewCell: CellType { } extension UIView: ViewType { } extension UICollectionView { // MARK: Cell /// Registers a generic cell for use in creating new collection view cells. public func register(_ cell: ReusableCell) { if let nib = cell.nib { self.register(nib, forCellWithReuseIdentifier: cell.identifier) } else { self.register(Cell.self, forCellWithReuseIdentifier: cell.identifier) } } /// Returns a generic reusable cell located by its identifier. public func dequeue(_ cell: ReusableCell, for indexPath: IndexPath) -> Cell { return self.dequeueReusableCell(withReuseIdentifier: cell.identifier, for: indexPath) as! Cell } // MARK: Supplementary View /// Registers a generic view for use in creating new supplementary views for the collection view. public func register(_ view: ReusableView, kind: SupplementaryViewKind) { self.register(view, kind: kind.rawValue) } /// Registers a generic view for use in creating new supplementary views for the collection view. public func register(_ view: ReusableView, kind: String) { if let nib = view.nib { self.register(nib, forSupplementaryViewOfKind: kind, withReuseIdentifier: view.identifier) } else { self.register(View.self, forSupplementaryViewOfKind: kind, withReuseIdentifier: view.identifier) } } /// Returns a generic reusable supplementary view located by its identifier and kind. public func dequeue(_ view: ReusableView, kind: String, for indexPath: IndexPath) -> View { return self.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: view.identifier, for: indexPath) as! View } /// Returns a generic reusable supplementary view located by its identifier and kind. public func dequeue(_ view: ReusableView, kind: SupplementaryViewKind, for indexPath: IndexPath) -> View { return self.dequeue(view, kind: kind.rawValue, for: indexPath) } } #endif ================================================ FILE: Pods/ReusableKit/Sources/ReusableKit/UITableView+ReusableKit.swift ================================================ #if os(iOS) import UIKit extension UITableViewCell: CellType { } extension UITableView { // MARK: Cell /// Registers a generic cell for use in creating new table cells. public func register(_ cell: ReusableCell) { if let nib = cell.nib { self.register(nib, forCellReuseIdentifier: cell.identifier) } else { self.register(Cell.self, forCellReuseIdentifier: cell.identifier) } } /// Returns a generic reusable cell located by its identifier. public func dequeue(_ cell: ReusableCell) -> Cell? { return self.dequeueReusableCell(withIdentifier: cell.identifier) as? Cell } /// Returns a generic reusable cell located by its identifier. public func dequeue(_ cell: ReusableCell, for indexPath: IndexPath) -> Cell { return self.dequeueReusableCell(withIdentifier: cell.identifier, for: indexPath) as! Cell } // MARK: View /// Registers a generic view for use in creating new table header or footer views. public func register(_ cell: ReusableView) { if let nib = cell.nib { self.register(nib, forHeaderFooterViewReuseIdentifier: cell.identifier) } else { self.register(View.self, forHeaderFooterViewReuseIdentifier: cell.identifier) } } /// Returns a generic reusable header of footer view located by its identifier. public func dequeue(_ view: ReusableView) -> View? { return self.dequeueReusableHeaderFooterView(withIdentifier: view.identifier) as? View } } #endif ================================================ FILE: Pods/ReusableKit/Sources/RxReusableKit/UICollectionView+RxReusableKit.swift ================================================ #if !COCOAPODS import ReusableKit #endif import RxCocoa import RxSwift #if os(iOS) import UIKit extension Reactive where Base: UICollectionView { public func items( _ reusableCell: ReusableCell ) -> (_ source: O) -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) -> Disposable where O.E == S { return { source in return { configureCell in return self.items(cellIdentifier: reusableCell.identifier, cellType: Cell.self)(source)(configureCell) } } } } #endif ================================================ FILE: Pods/ReusableKit/Sources/RxReusableKit/UITableView+RxReusableKit.swift ================================================ #if !COCOAPODS import ReusableKit #endif import RxCocoa import RxSwift #if os(iOS) import UIKit extension Reactive where Base: UITableView { public func items( _ reusableCell: ReusableCell ) -> (_ source: O) -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) -> Disposable where O.E == S { return { source in return { configureCell in return self.items(cellIdentifier: reusableCell.identifier, cellType: Cell.self)(source)(configureCell) } } } } #endif ================================================ FILE: Pods/RxAlamofire/LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2015 Junior B. 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. ================================================ FILE: Pods/RxAlamofire/README.md ================================================ RxAlamofire === RxAlamofire is a [RxSwift](https://github.com/ReactiveX/RxSwift) wrapper around the elegant HTTP networking in Swift [Alamofire](https://github.com/Alamofire/Alamofire). ## Getting Started Wrapping RxSwift around Alamofire makes working with network requests a smoother and nicer task. Alamofire is a very powerful framework and RxSwift add the ability to compose responses in a simple and effective way. A basic usage is (considering a simple currency converter): ```swift let formatter = NSNumberFormatter() formatter.numberStyle = .CurrencyStyle formatter.currencyCode = "USD" if let fromValue = NSNumberFormatter().numberFromString(self.fromTextField.text!) { RxAlamofire.requestJSON(.get, sourceStringURL) .debug() .subscribe(onNext: { [weak self] (r, json) in if let dict = json as? [String: AnyObject] { let valDict = dict["rates"] as! Dictionary if let conversionRate = valDict["USD"] as? Float { self?.toTextField.text = formatter .string(from: NSNumber(value: conversionRate * fromValue)) } } }, onError: { [weak self] (error) in self?.displayError(error as NSError) }) .addDisposableTo(disposeBag) } else { self.toTextField.text = "Invalid Input!" } ``` ## Example Usages Currently, the library features the following extensions: ```swift let stringURL = "" // MARK: NSURLSession simple and fast let session = NSURLSession.sharedSession() _ = session.rx .json(.get, stringURL) .observeOn(MainScheduler.instance) .subscribe { print($0) } _ = session .rx.data(.get, stringURL) .observeOn(MainScheduler.instance) .subscribe { print($0) } // MARK: With Alamofire engine _ = json(.get, stringURL) .observeOn(MainScheduler.instance) .subscribe { print($0) } _ = request(.get, stringURL) .flatMap { request in return request.validate(statusCode: 200..<300) .validate(contentType: ["text/json"]) .rx.json() } .observeOn(MainScheduler.instance) .subscribe { print($0) } // progress _ = request(.get, stringURL) .flatMap { $0 .validate(statusCode: 200 ..< 300) .validate(contentType: ["text/json"]) .rx.progress() } .observeOn(MainScheduler.instance) .subscribe { print($0) } // just fire upload and display progress _ = upload(Data(), urlRequest: try! RxAlamofire.urlRequest(.get, stringURL)) .flatMap { $0 .validate(statusCode: 200 ..< 300) .validate(contentType: ["text/json"]) .rx.progress() } .observeOn(MainScheduler.instance) .subscribe { print($0) } // progress and final result // uploading files with progress showing is processing intensive operation anyway, so // this doesn't add much overhead _ = request(.get, stringURL) .flatMap { request -> Observable<(Data?, RxProgress)> in let validatedRequest = request .validate(statusCode: 200 ..< 300) .validate(contentType: ["text/json"]) let dataPart = validatedRequest .rx.data() .map { d -> Data? in d } .startWith(nil as Data?) let progressPart = validatedRequest.rx.progress() return Observable.combineLatest(dataPart, progressPart) { ($0, $1) } } .observeOn(MainScheduler.instance) .subscribe { print($0) } // MARK: Alamofire manager // same methods with any alamofire manager let manager = Manager.sharedInstance // simple case _ = manager.rx.json(.get, stringURL) .observeOn(MainScheduler.instance) .subscribe { print($0) } // URLHTTPResponse + JSON _ = manager.rx.responseJSON(.get, stringURL) .observeOn(MainScheduler.instance) .subscribe { print($0) } // URLHTTPResponse + String _ = manager.rx.responseString(.get, stringURL) .observeOn(MainScheduler.instance) .subscribe { print($0) } // URLHTTPResponse + Validation + String _ = manager.rx.request(.get, stringURL) .flatMap { $0 .validate(statusCode: 200 ..< 300) .validate(contentType: ["text/json"]) .rx.string() } .observeOn(MainScheduler.instance) .subscribe { print($0) } // URLHTTPResponse + Validation + URLHTTPResponse + String _ = manager.rx.request(.get, stringURL) .flatMap { $0 .validate(statusCode: 200 ..< 300) .validate(contentType: ["text/json"]) .rx.responseString() } .observeOn(MainScheduler.instance) .subscribe { print($0) } // URLHTTPResponse + Validation + URLHTTPResponse + String + Progress _ = manager.rx.request(.get, stringURL) .flatMap { request -> Observable<(String?, RxProgress)> in let validatedRequest = request .validate(statusCode: 200 ..< 300) .validate(contentType: ["text/something"]) let stringPart = validatedRequest .rx.string() .map { d -> String? in d } .startWith(nil as String?) let progressPart = validatedRequest.rx.progress() return Observable.combineLatest(stringPart, progressPart) { ($0, $1) } } .observeOn(MainScheduler.instance) .subscribe { print($0) } ``` ## Installation There are two ways to install RxAlamofire ### CocoaPods Just add to your project's `Podfile`: ``` pod 'RxAlamofire' ``` ### Carthage Add following to `Cartfile`: ``` github "RxSwiftCommunity/RxAlamofire" "master" ``` ### Manually To manual install this extension you should get the `RxAlamofire/Source/RxAlamofire.swift` imported into your project, alongside RxSwift and Alamofire. ## Requirements RxAlamofire requires Swift 4.0 and dedicated versions of Alamofire (4.5.1) and RxSwift (4.0.0-beta.0). ================================================ FILE: Pods/RxAlamofire/Sources/RxAlamofire.swift ================================================ // // RxAlamofire.swift // RxAlamofire // // Created by Junior B. (@bontojr) on 23/08/15. // Developed with the kind help of Krunoslav Zaher (@KrunoslavZaher) // // Updated by Ivan Đikić for the latest version of Alamofire(3) and RxSwift(2) on 21/10/15 // Updated by Krunoslav Zaher to better wrap Alamofire (3) on 1/10/15 // // Copyright © 2015 Bonto.ch. All rights reserved. // import Foundation import Alamofire import RxSwift /// Default instance of unknown error public let RxAlamofireUnknownError = NSError(domain: "RxAlamofireDomain", code: -1, userInfo: nil) // MARK: Convenience functions /** Creates a NSMutableURLRequest using all necessary parameters. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An instance of `NSMutableURLRequest` */ public func urlRequest(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) throws -> Foundation.URLRequest { var mutableURLRequest = Foundation.URLRequest(url: try url.asURL()) mutableURLRequest.httpMethod = method.rawValue if let headers = headers { for (headerField, headerValue) in headers { mutableURLRequest.setValue(headerValue, forHTTPHeaderField: headerField) } } if let parameters = parameters { mutableURLRequest = try encoding.encode(mutableURLRequest, with: parameters) } return mutableURLRequest } // MARK: Request /** Creates an observable of the generated `Request`. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of a the `Request` */ public func request(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) -> Observable { return SessionManager.default.rx.request( method, url, parameters: parameters, encoding: encoding, headers: headers ) } /** Creates an observable of the generated `Request`. - parameter urlRequest: An object adopting `URLRequestConvertible` - returns: An observable of a the `Request` */ public func request(_ urlRequest: URLRequestConvertible) -> Observable { return SessionManager.default.rx.request(urlRequest: urlRequest) } // MARK: data /** Creates an observable of the `(NSHTTPURLResponse, NSData)` instance. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of a tuple containing `(NSHTTPURLResponse, NSData)` */ public func requestData(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) -> Observable<(HTTPURLResponse, Data)> { return SessionManager.default.rx.responseData( method, url, parameters: parameters, encoding: encoding, headers: headers ) } /** Creates an observable of the `(NSHTTPURLResponse, NSData)` instance. - parameter urlRequest: An object adopting `URLRequestConvertible` - returns: An observable of a tuple containing `(NSHTTPURLResponse, NSData)` */ public func requestData(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, Data)> { return request(urlRequest).flatMap { $0.rx.responseData() } } /** Creates an observable of the returned data. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of `NSData` */ public func data(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) -> Observable { return SessionManager.default.rx.data( method, url, parameters: parameters, encoding: encoding, headers: headers ) } // MARK: string /** Creates an observable of the returned decoded string and response. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the tuple `(NSHTTPURLResponse, String)` */ public func requestString(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) -> Observable<(HTTPURLResponse, String)> { return SessionManager.default.rx.responseString( method, url, parameters: parameters, encoding: encoding, headers: headers ) } /** Creates an observable of the returned decoded string and response. - parameter urlRequest: An object adopting `URLRequestConvertible` - returns: An observable of the tuple `(NSHTTPURLResponse, String)` */ public func requestString(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, String)> { return request(urlRequest).flatMap { $0.rx.responseString() } } /** Creates an observable of the returned decoded string. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of `String` */ public func string(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) -> Observable { return SessionManager.default.rx.string( method, url, parameters: parameters, encoding: encoding, headers: headers ) } // MARK: JSON /** Creates an observable of the returned decoded JSON as `AnyObject` and the response. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)` */ public func requestJSON(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) -> Observable<(HTTPURLResponse, Any)> { return SessionManager.default.rx.responseJSON( method, url, parameters: parameters, encoding: encoding, headers: headers ) } /** Creates an observable of the returned decoded JSON as `AnyObject` and the response. - parameter urlRequest: An object adopting `URLRequestConvertible` - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)` */ public func requestJSON(_ urlRequest: URLRequestConvertible) -> Observable<(HTTPURLResponse, Any)> { return request(urlRequest).flatMap { $0.rx.responseJSON() } } /** Creates an observable of the returned decoded JSON. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the decoded JSON as `Any` */ public func json(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil) -> Observable { return SessionManager.default.rx.json( method, url, parameters: parameters, encoding: encoding, headers: headers ) } // MARK: Upload /** Returns an observable of a request using the shared manager instance to upload a specific file to a specified URL. The request is started immediately. - parameter urlRequest: The request object to start the upload. - paramenter file: An instance of NSURL holding the information of the local file. - returns: The observable of `UploadRequest` for the created request. */ public func upload(_ file: URL, urlRequest: URLRequestConvertible) -> Observable { return SessionManager.default.rx.upload(file, urlRequest: urlRequest) } /** Returns an observable of a request using the shared manager instance to upload any data to a specified URL. The request is started immediately. - parameter urlRequest: The request object to start the upload. - paramenter data: An instance of NSData holdint the data to upload. - returns: The observable of `UploadRequest` for the created request. */ public func upload(_ data: Data, urlRequest: URLRequestConvertible) -> Observable { return SessionManager.default.rx.upload(data , urlRequest: urlRequest) } /** Returns an observable of a request using the shared manager instance to upload any stream to a specified URL. The request is started immediately. - parameter urlRequest: The request object to start the upload. - paramenter stream: The stream to upload. - returns: The observable of `Request` for the created upload request. */ public func upload(_ stream: InputStream, urlRequest: URLRequestConvertible) -> Observable { return SessionManager.default.rx.upload(stream, urlRequest: urlRequest) } // MARK: Download /** Creates a download request using the shared manager instance for the specified URL request. - parameter urlRequest: The URL request. - parameter destination: The closure used to determine the destination of the downloaded file. - returns: The observable of `DownloadRequest` for the created download request. */ public func download(_ urlRequest: URLRequestConvertible, to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable { return SessionManager.default.rx.download(urlRequest, to: destination) } // MARK: Resume Data /** Creates a request using the shared manager instance for downloading from the resume data produced from a previous request cancellation. - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional information. - parameter destination: The closure used to determine the destination of the downloaded file. - returns: The observable of `Request` for the created download request. */ public func download(resumeData: Data, to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable { return SessionManager.default.rx.download(resumeData: resumeData, to: destination) } // MARK: Manager - Extension of Manager extension SessionManager: ReactiveCompatible { } protocol RxAlamofireRequest { func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void) func resume() func cancel() } protocol RxAlamofireResponse { var error: Error? {get} } extension DefaultDataResponse: RxAlamofireResponse {} extension DefaultDownloadResponse: RxAlamofireResponse {} extension DataRequest: RxAlamofireRequest { func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void) { response { (response) in completionHandler(response) } } } extension DownloadRequest: RxAlamofireRequest { func responseWith(completionHandler: @escaping (RxAlamofireResponse) -> Void) { response { (response) in completionHandler(response) } } } extension Reactive where Base: SessionManager { // MARK: Generic request convenience /** Creates an observable of the DataRequest. - parameter createRequest: A function used to create a `Request` using a `Manager` - returns: A generic observable of created data request */ func request(_ createRequest: @escaping (SessionManager) throws -> R) -> Observable { return Observable.create { observer -> Disposable in let request: R do { request = try createRequest(self.base) observer.on(.next(request)) request.responseWith(completionHandler: { (response) in if let error = response.error { observer.on(.error(error)) } else { observer.on(.completed) } }) if !self.base.startRequestsImmediately { request.resume() } return Disposables.create { request.cancel() } } catch let error { observer.on(.error(error)) return Disposables.create() } } } /** Creates an observable of the `Request`. - parameter method: Alamofire method object - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the `Request` */ public func request(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil ) -> Observable { return request { manager in return manager.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers) } } /** Creates an observable of the `Request`. - parameter URLRequest: An object adopting `URLRequestConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the `Request` */ public func request(urlRequest: URLRequestConvertible) -> Observable { return request { manager in return manager.request(urlRequest) } } // MARK: data /** Creates an observable of the data. - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the tuple `(NSHTTPURLResponse, NSData)` */ public func responseData(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil ) -> Observable<(HTTPURLResponse, Data)> { return request( method, url, parameters: parameters, encoding: encoding, headers: headers ).flatMap { $0.rx.responseData() } } /** Creates an observable of the data. - parameter URLRequest: An object adopting `URLRequestConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of `NSData` */ public func data(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil ) -> Observable { return request( method, url, parameters: parameters, encoding: encoding, headers: headers ).flatMap { $0.rx.data() } } // MARK: string /** Creates an observable of the tuple `(NSHTTPURLResponse, String)`. - parameter url: An object adopting `URLRequestConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the tuple `(NSHTTPURLResponse, String)` */ public func responseString(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil ) -> Observable<(HTTPURLResponse, String)> { return request( method, url, parameters: parameters, encoding: encoding, headers: headers ).flatMap { $0.rx.responseString() } } /** Creates an observable of the data encoded as String. - parameter url: An object adopting `URLConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of `String` */ public func string(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil ) -> Observable { return request( method, url, parameters: parameters, encoding: encoding, headers: headers ) .flatMap { (request) -> Observable in return request.rx.string() } } // MARK: JSON /** Creates an observable of the data decoded from JSON and processed as tuple `(NSHTTPURLResponse, AnyObject)`. - parameter url: An object adopting `URLRequestConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of the tuple `(NSHTTPURLResponse, AnyObject)` */ public func responseJSON(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil ) -> Observable<(HTTPURLResponse, Any)> { return request(method, url, parameters: parameters, encoding: encoding, headers: headers ).flatMap { $0.rx.responseJSON() } } /** Creates an observable of the data decoded from JSON and processed as `AnyObject`. - parameter URLRequest: An object adopting `URLRequestConvertible` - parameter parameters: A dictionary containing all necessary options - parameter encoding: The kind of encoding used to process parameters - parameter header: A dictionary containing all the additional headers - returns: An observable of `AnyObject` */ public func json(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [String: Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: [String: String]? = nil ) -> Observable { return request( method, url, parameters: parameters, encoding: encoding, headers: headers ).flatMap { $0.rx.json() } } // MARK: Upload /** Returns an observable of a request using the shared manager instance to upload a specific file to a specified URL. The request is started immediately. - parameter urlRequest: The request object to start the upload. - paramenter file: An instance of NSURL holding the information of the local file. - returns: The observable of `AnyObject` for the created request. */ public func upload(_ file: URL, urlRequest: URLRequestConvertible) -> Observable { return request { manager in return manager.upload(file, with: urlRequest) } } /** Returns an observable of a request using the shared manager instance to upload any data to a specified URL. The request is started immediately. - parameter urlRequest: The request object to start the upload. - paramenter data: An instance of Data holdint the data to upload. - returns: The observable of `UploadRequest` for the created request. */ public func upload(_ data: Data, urlRequest: URLRequestConvertible) -> Observable { return request { manager in return manager.upload(data, with: urlRequest) } } /** Returns an observable of a request using the shared manager instance to upload any stream to a specified URL. The request is started immediately. - parameter urlRequest: The request object to start the upload. - paramenter stream: The stream to upload. - returns: The observable of `(NSData?, RxProgress)` for the created upload request. */ public func upload(_ stream: InputStream, urlRequest: URLRequestConvertible) -> Observable { return request { manager in return manager.upload(stream, with: urlRequest) } } // MARK: Download /** Creates a download request using the shared manager instance for the specified URL request. - parameter urlRequest: The URL request. - parameter destination: The closure used to determine the destination of the downloaded file. - returns: The observable of `(NSData?, RxProgress)` for the created download request. */ public func download(_ urlRequest: URLRequestConvertible, to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable { return request { manager in return manager.download(urlRequest, to: destination) } } /** Creates a request using the shared manager instance for downloading with a resume data produced from a previous request cancellation. - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional information. - parameter destination: The closure used to determine the destination of the downloaded file. - returns: The observable of `(NSData?, RxProgress)` for the created download request. */ public func download(resumeData: Data, to destination: @escaping DownloadRequest.DownloadFileDestination) -> Observable { return request { manager in return manager.download(resumingWith: resumeData, to: destination) } } } // MARK: Request - Common Response Handlers extension ObservableType where E == DataRequest { public func responseJSON() -> Observable> { return self.flatMap { $0.rx.responseJSON() } } } extension Request: ReactiveCompatible { } extension Reactive where Base: DataRequest { // MARK: Defaults /// - returns: A validated request based on the status code func validateSuccessfulResponse() -> DataRequest { return self.base.validate(statusCode: 200 ..< 300) } /** Transform a request into an observable of the response and serialized object. - parameter queue: The dispatch queue to use. - parameter responseSerializer: The the serializer. - returns: The observable of `(NSHTTPURLResponse, T.SerializedObject)` for the created download request. */ public func responseResult(queue: DispatchQueue? = nil, responseSerializer: T) -> Observable<(HTTPURLResponse, T.SerializedObject)> { return Observable.create { observer in let dataRequest = self.base .response(queue: queue, responseSerializer: responseSerializer) { (packedResponse) -> Void in switch packedResponse.result { case .success(let result): if let httpResponse = packedResponse.response { observer.on(.next((httpResponse, result))) observer.on(.completed) } else { observer.on(.error(RxAlamofireUnknownError)) } case .failure(let error): observer.on(.error(error as Error)) } } return Disposables.create { dataRequest.cancel() } } } public func responseJSON() -> Observable> { return Observable.create { observer in let request = self.base request.responseJSON { response in if let error = response.result.error { observer.on(.error(error)) } else { observer.on(.next(response)) observer.on(.completed) } } return Disposables.create { request.cancel() } } } /** Transform a request into an observable of the serialized object. - parameter queue: The dispatch queue to use. - parameter responseSerializer: The the serializer. - returns: The observable of `T.SerializedObject` for the created download request. */ public func result( queue: DispatchQueue? = nil, responseSerializer: T) -> Observable { return Observable.create { observer in let dataRequest = self.validateSuccessfulResponse() .response(queue: queue, responseSerializer: responseSerializer) { (packedResponse) -> Void in switch packedResponse.result { case .success(let result): if let _ = packedResponse.response { observer.on(.next(result)) observer.on(.completed) } else { observer.on(.error(RxAlamofireUnknownError)) } case .failure(let error): observer.on(.error(error as Error)) } } return Disposables.create { dataRequest.cancel() } } } /** Returns an `Observable` of NSData for the current request. - parameter cancelOnDispose: Indicates if the request has to be canceled when the observer is disposed, **default:** `false` - returns: An instance of `Observable` */ public func responseData() -> Observable<(HTTPURLResponse, Data)> { return responseResult(responseSerializer: DataRequest.dataResponseSerializer()) } public func data() -> Observable { return result(responseSerializer: DataRequest.dataResponseSerializer()) } /** Returns an `Observable` of a String for the current request - parameter encoding: Type of the string encoding, **default:** `nil` - returns: An instance of `Observable` */ public func responseString(encoding: String.Encoding? = nil) -> Observable<(HTTPURLResponse, String)> { return responseResult(responseSerializer: Base.stringResponseSerializer(encoding: encoding)) } public func string(encoding: String.Encoding? = nil) -> Observable { return result(responseSerializer: Base.stringResponseSerializer(encoding: encoding)) } /** Returns an `Observable` of a serialized JSON for the current request. - parameter options: Reading options for JSON decoding process, **default:** `.AllowFragments` - returns: An instance of `Observable` */ public func responseJSON(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable<(HTTPURLResponse, Any)> { return responseResult(responseSerializer: Base.jsonResponseSerializer(options: options)) } /** Returns an `Observable` of a serialized JSON for the current request. - parameter options: Reading options for JSON decoding process, **default:** `.AllowFragments` - returns: An instance of `Observable` */ public func json(options: JSONSerialization.ReadingOptions = .allowFragments) -> Observable { return result(responseSerializer: Base.jsonResponseSerializer(options: options)) } /** Returns and `Observable` of a serialized property list for the current request. - parameter options: Property list reading options, **default:** `NSPropertyListReadOptions()` - returns: An instance of `Observable` */ public func responsePropertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable<(HTTPURLResponse, Any)> { return responseResult(responseSerializer: Base.propertyListResponseSerializer(options: options)) } public func propertyList(options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) -> Observable { return result(responseSerializer: Base.propertyListResponseSerializer(options: options)) } // MARK: Request - Upload and download progress /** Returns an `Observable` for the current progress status. Parameters on observed tuple: 1. bytes written so far. 1. total bytes to write. - returns: An instance of `Observable` */ public func progress() -> Observable { return Observable.create { observer in self.base.downloadProgress { progress in let rxProgress = RxProgress(bytesWritten: progress.completedUnitCount, totalBytes: progress.totalUnitCount) observer.on(.next(rxProgress)) if rxProgress.bytesWritten >= rxProgress.totalBytes { observer.on(.completed) } } return Disposables.create() } // warm up a bit :) .startWith(RxProgress(bytesWritten: 0, totalBytes: 0)) } } extension Reactive where Base: DownloadRequest { /** Returns an `Observable` for the current progress status. Parameters on observed tuple: 1. bytes written so far. 1. total bytes to write. - returns: An instance of `Observable` */ public func progress() -> Observable { return Observable.create { observer in self.base.downloadProgress { progress in let rxProgress = RxProgress(bytesWritten: progress.completedUnitCount, totalBytes: progress.totalUnitCount) observer.on(.next(rxProgress)) if rxProgress.bytesWritten >= rxProgress.totalBytes { observer.on(.completed) } } return Disposables.create() } // warm up a bit :) .startWith(RxProgress(bytesWritten: 0, totalBytes: 0)) } } // MARK: RxProgress public struct RxProgress { public let bytesWritten: Int64 public let totalBytes: Int64 public init(bytesWritten: Int64, totalBytes: Int64) { self.bytesWritten = bytesWritten self.totalBytes = totalBytes } } extension RxProgress { public var bytesRemaining: Int64 { return totalBytes - bytesWritten } public var completed: Float { if totalBytes > 0 { return Float(bytesWritten) / Float(totalBytes) } else { return 0 } } } extension RxProgress: Equatable {} public func ==(lhs: RxProgress, rhs: RxProgress) -> Bool { return lhs.bytesWritten == rhs.bytesWritten && lhs.totalBytes == rhs.totalBytes } ================================================ FILE: Pods/RxCocoa/LICENSE.md ================================================ **The MIT License** **Copyright © 2015 Krunoslav Zaher** **All rights reserved.** 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. ================================================ FILE: Pods/RxCocoa/Platform/DataStructures/Bag.swift ================================================ // // Bag.swift // Platform // // Created by Krunoslav Zaher on 2/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import Swift let arrayDictionaryMaxSize = 30 struct BagKey { /** Unique identifier for object added to `Bag`. It's underlying type is UInt64. If we assume there in an idealized CPU that works at 4GHz, it would take ~150 years of continuous running time for it to overflow. */ fileprivate let rawValue: UInt64 } /** Data structure that represents a bag of elements typed `T`. Single element can be stored multiple times. Time and space complexity of insertion an deletion is O(n). It is suitable for storing small number of elements. */ struct Bag : CustomDebugStringConvertible { /// Type of identifier for inserted elements. typealias KeyType = BagKey typealias Entry = (key: BagKey, value: T) fileprivate var _nextKey: BagKey = BagKey(rawValue: 0) // data // first fill inline variables var _key0: BagKey? = nil var _value0: T? = nil // then fill "array dictionary" var _pairs = ContiguousArray() // last is sparse dictionary var _dictionary: [BagKey : T]? = nil var _onlyFastPath = true /// Creates new empty `Bag`. init() { } /** Inserts `value` into bag. - parameter element: Element to insert. - returns: Key that can be used to remove element from bag. */ mutating func insert(_ element: T) -> BagKey { let key = _nextKey _nextKey = BagKey(rawValue: _nextKey.rawValue &+ 1) if _key0 == nil { _key0 = key _value0 = element return key } _onlyFastPath = false if _dictionary != nil { _dictionary![key] = element return key } if _pairs.count < arrayDictionaryMaxSize { _pairs.append((key: key, value: element)) return key } if _dictionary == nil { _dictionary = [:] } _dictionary![key] = element return key } /// - returns: Number of elements in bag. var count: Int { let dictionaryCount: Int = _dictionary?.count ?? 0 return (_value0 != nil ? 1 : 0) + _pairs.count + dictionaryCount } /// Removes all elements from bag and clears capacity. mutating func removeAll() { _key0 = nil _value0 = nil _pairs.removeAll(keepingCapacity: false) _dictionary?.removeAll(keepingCapacity: false) } /** Removes element with a specific `key` from bag. - parameter key: Key that identifies element to remove from bag. - returns: Element that bag contained, or nil in case element was already removed. */ mutating func removeKey(_ key: BagKey) -> T? { if _key0 == key { _key0 = nil let value = _value0! _value0 = nil return value } if let existingObject = _dictionary?.removeValue(forKey: key) { return existingObject } for i in 0 ..< _pairs.count { if _pairs[i].key == key { let value = _pairs[i].value _pairs.remove(at: i) return value } } return nil } } extension Bag { /// A textual representation of `self`, suitable for debugging. var debugDescription : String { return "\(self.count) elements in Bag" } } extension Bag { /// Enumerates elements inside the bag. /// /// - parameter action: Enumeration closure. func forEach(_ action: (T) -> Void) { if _onlyFastPath { if let value0 = _value0 { action(value0) } return } let value0 = _value0 let dictionary = _dictionary if let value0 = value0 { action(value0) } for i in 0 ..< _pairs.count { action(_pairs[i].value) } if dictionary?.count ?? 0 > 0 { for element in dictionary!.values { action(element) } } } } extension BagKey: Hashable { var hashValue: Int { return rawValue.hashValue } } func ==(lhs: BagKey, rhs: BagKey) -> Bool { return lhs.rawValue == rhs.rawValue } ================================================ FILE: Pods/RxCocoa/Platform/DataStructures/InfiniteSequence.swift ================================================ // // InfiniteSequence.swift // Platform // // Created by Krunoslav Zaher on 6/13/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Sequence that repeats `repeatedValue` infinite number of times. struct InfiniteSequence : Sequence { typealias Element = E typealias Iterator = AnyIterator private let _repeatedValue: E init(repeatedValue: E) { _repeatedValue = repeatedValue } func makeIterator() -> Iterator { let repeatedValue = _repeatedValue return AnyIterator { return repeatedValue } } } ================================================ FILE: Pods/RxCocoa/Platform/DataStructures/PriorityQueue.swift ================================================ // // PriorityQueue.swift // Platform // // Created by Krunoslav Zaher on 12/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // struct PriorityQueue { private let _hasHigherPriority: (Element, Element) -> Bool private let _isEqual: (Element, Element) -> Bool fileprivate var _elements = [Element]() init(hasHigherPriority: @escaping (Element, Element) -> Bool, isEqual: @escaping (Element, Element) -> Bool) { _hasHigherPriority = hasHigherPriority _isEqual = isEqual } mutating func enqueue(_ element: Element) { _elements.append(element) bubbleToHigherPriority(_elements.count - 1) } func peek() -> Element? { return _elements.first } var isEmpty: Bool { return _elements.count == 0 } mutating func dequeue() -> Element? { guard let front = peek() else { return nil } removeAt(0) return front } mutating func remove(_ element: Element) { for i in 0 ..< _elements.count { if _isEqual(_elements[i], element) { removeAt(i) return } } } private mutating func removeAt(_ index: Int) { let removingLast = index == _elements.count - 1 if !removingLast { #if swift(>=3.2) _elements.swapAt(index, _elements.count - 1) #else swap(&_elements[index], &_elements[_elements.count - 1]) #endif } _ = _elements.popLast() if !removingLast { bubbleToHigherPriority(index) bubbleToLowerPriority(index) } } private mutating func bubbleToHigherPriority(_ initialUnbalancedIndex: Int) { precondition(initialUnbalancedIndex >= 0) precondition(initialUnbalancedIndex < _elements.count) var unbalancedIndex = initialUnbalancedIndex while unbalancedIndex > 0 { let parentIndex = (unbalancedIndex - 1) / 2 guard _hasHigherPriority(_elements[unbalancedIndex], _elements[parentIndex]) else { break } #if swift(>=3.2) _elements.swapAt(unbalancedIndex, parentIndex) #else swap(&_elements[unbalancedIndex], &_elements[parentIndex]) #endif unbalancedIndex = parentIndex } } private mutating func bubbleToLowerPriority(_ initialUnbalancedIndex: Int) { precondition(initialUnbalancedIndex >= 0) precondition(initialUnbalancedIndex < _elements.count) var unbalancedIndex = initialUnbalancedIndex while true { let leftChildIndex = unbalancedIndex * 2 + 1 let rightChildIndex = unbalancedIndex * 2 + 2 var highestPriorityIndex = unbalancedIndex if leftChildIndex < _elements.count && _hasHigherPriority(_elements[leftChildIndex], _elements[highestPriorityIndex]) { highestPriorityIndex = leftChildIndex } if rightChildIndex < _elements.count && _hasHigherPriority(_elements[rightChildIndex], _elements[highestPriorityIndex]) { highestPriorityIndex = rightChildIndex } guard highestPriorityIndex != unbalancedIndex else { break } #if swift(>=3.2) _elements.swapAt(highestPriorityIndex, unbalancedIndex) #else swap(&_elements[highestPriorityIndex], &_elements[unbalancedIndex]) #endif unbalancedIndex = highestPriorityIndex } } } extension PriorityQueue : CustomDebugStringConvertible { var debugDescription: String { return _elements.debugDescription } } ================================================ FILE: Pods/RxCocoa/Platform/DataStructures/Queue.swift ================================================ // // Queue.swift // Platform // // Created by Krunoslav Zaher on 3/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /** Data structure that represents queue. Complexity of `enqueue`, `dequeue` is O(1) when number of operations is averaged over N operations. Complexity of `peek` is O(1). */ struct Queue: Sequence { /// Type of generator. typealias Generator = AnyIterator private let _resizeFactor = 2 private var _storage: ContiguousArray private var _count = 0 private var _pushNextIndex = 0 private let _initialCapacity: Int /** Creates new queue. - parameter capacity: Capacity of newly created queue. */ init(capacity: Int) { _initialCapacity = capacity _storage = ContiguousArray(repeating: nil, count: capacity) } private var dequeueIndex: Int { let index = _pushNextIndex - count return index < 0 ? index + _storage.count : index } /// - returns: Is queue empty. var isEmpty: Bool { return count == 0 } /// - returns: Number of elements inside queue. var count: Int { return _count } /// - returns: Element in front of a list of elements to `dequeue`. func peek() -> T { precondition(count > 0) return _storage[dequeueIndex]! } mutating private func resizeTo(_ size: Int) { var newStorage = ContiguousArray(repeating: nil, count: size) let count = _count let dequeueIndex = self.dequeueIndex let spaceToEndOfQueue = _storage.count - dequeueIndex // first batch is from dequeue index to end of array let countElementsInFirstBatch = Swift.min(count, spaceToEndOfQueue) // second batch is wrapped from start of array to end of queue let numberOfElementsInSecondBatch = count - countElementsInFirstBatch newStorage[0 ..< countElementsInFirstBatch] = _storage[dequeueIndex ..< (dequeueIndex + countElementsInFirstBatch)] newStorage[countElementsInFirstBatch ..< (countElementsInFirstBatch + numberOfElementsInSecondBatch)] = _storage[0 ..< numberOfElementsInSecondBatch] _count = count _pushNextIndex = count _storage = newStorage } /// Enqueues `element`. /// /// - parameter element: Element to enqueue. mutating func enqueue(_ element: T) { if count == _storage.count { resizeTo(Swift.max(_storage.count, 1) * _resizeFactor) } _storage[_pushNextIndex] = element _pushNextIndex += 1 _count += 1 if _pushNextIndex >= _storage.count { _pushNextIndex -= _storage.count } } private mutating func dequeueElementOnly() -> T { precondition(count > 0) let index = dequeueIndex defer { _storage[index] = nil _count -= 1 } return _storage[index]! } /// Dequeues element or throws an exception in case queue is empty. /// /// - returns: Dequeued element. mutating func dequeue() -> T? { if self.count == 0 { return nil } defer { let downsizeLimit = _storage.count / (_resizeFactor * _resizeFactor) if _count < downsizeLimit && downsizeLimit >= _initialCapacity { resizeTo(_storage.count / _resizeFactor) } } return dequeueElementOnly() } /// - returns: Generator of contained elements. func makeIterator() -> AnyIterator { var i = dequeueIndex var count = _count return AnyIterator { if count == 0 { return nil } defer { count -= 1 i += 1 } if i >= self._storage.count { i -= self._storage.count } return self._storage[i] } } } ================================================ FILE: Pods/RxCocoa/Platform/DispatchQueue+Extensions.swift ================================================ // // DispatchQueue+Extensions.swift // Platform // // Created by Krunoslav Zaher on 10/22/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Dispatch extension DispatchQueue { private static var token: DispatchSpecificKey<()> = { let key = DispatchSpecificKey<()>() DispatchQueue.main.setSpecific(key: key, value: ()) return key }() static var isMain: Bool { return DispatchQueue.getSpecific(key: token) != nil } } ================================================ FILE: Pods/RxCocoa/Platform/Platform.Darwin.swift ================================================ // // Platform.Darwin.swift // Platform // // Created by Krunoslav Zaher on 12/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin import class Foundation.Thread import func Foundation.OSAtomicCompareAndSwap32Barrier import func Foundation.OSAtomicIncrement32Barrier import func Foundation.OSAtomicDecrement32Barrier import protocol Foundation.NSCopying typealias AtomicInt = Int32 fileprivate func castToUInt32Pointer(_ pointer: UnsafeMutablePointer) -> UnsafeMutablePointer { let raw = UnsafeMutableRawPointer(pointer) return raw.assumingMemoryBound(to: UInt32.self) } let AtomicCompareAndSwap = OSAtomicCompareAndSwap32Barrier let AtomicIncrement = OSAtomicIncrement32Barrier let AtomicDecrement = OSAtomicDecrement32Barrier func AtomicOr(_ mask: UInt32, _ theValue : UnsafeMutablePointer) -> Int32 { return OSAtomicOr32OrigBarrier(mask, castToUInt32Pointer(theValue)) } func AtomicFlagSet(_ mask: UInt32, _ theValue : UnsafeMutablePointer) -> Bool { // just used to create a barrier OSAtomicXor32OrigBarrier(0, castToUInt32Pointer(theValue)) return (theValue.pointee & Int32(mask)) != 0 } extension Thread { static func setThreadLocalStorageValue(_ value: T?, forKey key: NSCopying ) { let currentThread = Thread.current let threadDictionary = currentThread.threadDictionary if let newValue = value { threadDictionary[key] = newValue } else { threadDictionary[key] = nil } } static func getThreadLocalStorageValueForKey(_ key: NSCopying) -> T? { let currentThread = Thread.current let threadDictionary = currentThread.threadDictionary return threadDictionary[key] as? T } } extension AtomicInt { func valueSnapshot() -> Int32 { return self } } #endif ================================================ FILE: Pods/RxCocoa/Platform/Platform.Linux.swift ================================================ // // Platform.Linux.swift // Platform // // Created by Krunoslav Zaher on 12/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(Linux) import XCTest import Glibc import SwiftShims import class Foundation.Thread final class AtomicInt { typealias IntegerLiteralType = Int fileprivate var value: Int32 = 0 fileprivate var _lock = RecursiveLock() func lock() { _lock.lock() } func unlock() { _lock.unlock() } func valueSnapshot() -> Int32 { return value } } extension AtomicInt: ExpressibleByIntegerLiteral { convenience init(integerLiteral value: Int) { self.init() self.value = Int32(value) } } func >(lhs: AtomicInt, rhs: Int32) -> Bool { return lhs.value > rhs } func ==(lhs: AtomicInt, rhs: Int32) -> Bool { return lhs.value == rhs } func AtomicFlagSet(_ mask: UInt32, _ atomic: inout AtomicInt) -> Bool { atomic.lock(); defer { atomic.unlock() } return (atomic.value & Int32(mask)) != 0 } func AtomicOr(_ mask: UInt32, _ atomic: inout AtomicInt) -> Int32 { atomic.lock(); defer { atomic.unlock() } let value = atomic.value atomic.value |= Int32(mask) return value } func AtomicIncrement(_ atomic: inout AtomicInt) -> Int32 { atomic.lock(); defer { atomic.unlock() } atomic.value += 1 return atomic.value } func AtomicDecrement(_ atomic: inout AtomicInt) -> Int32 { atomic.lock(); defer { atomic.unlock() } atomic.value -= 1 return atomic.value } func AtomicCompareAndSwap(_ l: Int32, _ r: Int32, _ atomic: inout AtomicInt) -> Bool { atomic.lock(); defer { atomic.unlock() } if atomic.value == l { atomic.value = r return true } return false } extension Thread { static func setThreadLocalStorageValue(_ value: T?, forKey key: String) { let currentThread = Thread.current var threadDictionary = currentThread.threadDictionary if let newValue = value { threadDictionary[key] = newValue } else { threadDictionary[key] = nil } currentThread.threadDictionary = threadDictionary } static func getThreadLocalStorageValueForKey(_ key: String) -> T? { let currentThread = Thread.current let threadDictionary = currentThread.threadDictionary return threadDictionary[key] as? T } } #endif ================================================ FILE: Pods/RxCocoa/Platform/RecursiveLock.swift ================================================ // // RecursiveLock.swift // Platform // // Created by Krunoslav Zaher on 12/18/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import class Foundation.NSRecursiveLock #if TRACE_RESOURCES class RecursiveLock: NSRecursiveLock { override init() { _ = Resources.incrementTotal() super.init() } override func lock() { super.lock() _ = Resources.incrementTotal() } override func unlock() { super.unlock() _ = Resources.decrementTotal() } deinit { _ = Resources.decrementTotal() } } #else typealias RecursiveLock = NSRecursiveLock #endif ================================================ FILE: Pods/RxCocoa/README.md ================================================ Miss Electric Eel 2016 RxSwift: ReactiveX for Swift ====================================== [![Travis CI](https://travis-ci.org/ReactiveX/RxSwift.svg?branch=master)](https://travis-ci.org/ReactiveX/RxSwift) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux-333333.svg) ![pod](https://img.shields.io/cocoapods/v/RxSwift.svg) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) * RxSwift 4.x / Swift 4.x can be found in [**rxswift4.0-swift4.0** branch](https://github.com/ReactiveX/RxSwift/tree/rxswift4.0-swift4.0). * RxSwift 3.x / Swift 3.x can be found in [**master** branch](https://github.com/ReactiveX/RxSwift/tree/master). Rx is a [generic abstraction of computation](https://youtu.be/looJcaeboBY) expressed through `Observable` interface. This is a Swift version of [Rx](https://github.com/Reactive-Extensions/Rx.NET). It tries to port as many concepts from the original version as possible, but some concepts were adapted for more pleasant and performant integration with iOS/macOS environment. Cross platform documentation can be found on [ReactiveX.io](http://reactivex.io/). Like the original Rx, its intention is to enable easy composition of asynchronous operations and event/data streams. KVO observing, async operations and streams are all unified under [abstraction of sequence](Documentation/GettingStarted.md#observables-aka-sequences). This is the reason why Rx is so simple, elegant and powerful. ## I came here because I want to ... ###### ... understand * [why use rx?](Documentation/Why.md) * [the basics, getting started with RxSwift](Documentation/GettingStarted.md) * [traits](Documentation/Traits.md) - what are `Single`, `Completable`, `Maybe`, `Driver`, `ControlProperty`, and `Variable` ... and why do they exist? * [testing](Documentation/UnitTests.md) * [tips and common errors](Documentation/Tips.md) * [debugging](Documentation/GettingStarted.md#debugging) * [the math behind Rx](Documentation/MathBehindRx.md) * [what are hot and cold observable sequences?](Documentation/HotAndColdObservables.md) ###### ... install * Integrate RxSwift/RxCocoa with my app. [Installation Guide](#installation) ###### ... hack around * with the example app. [Running Example App](Documentation/ExampleApp.md) * with operators in playgrounds. [Playgrounds](Documentation/Playgrounds.md) ###### ... interact * All of this is great, but it would be nice to talk with other people using RxSwift and exchange experiences.
[![Slack channel](http://rxswift-slack.herokuapp.com/badge.svg)](http://rxswift-slack.herokuapp.com/) [Join Slack Channel](http://rxswift-slack.herokuapp.com) * Report a problem using the library. [Open an Issue With Bug Template](.github/ISSUE_TEMPLATE.md) * Request a new feature. [Open an Issue With Feature Request Template](Documentation/NewFeatureRequestTemplate.md) ###### ... compare * [with other libraries](Documentation/ComparisonWithOtherLibraries.md). ###### ... find compatible * libraries from [RxSwiftCommunity](https://github.com/RxSwiftCommunity). * [Pods using RxSwift](https://cocoapods.org/?q=uses%3Arxswift). ###### ... see the broader vision * Does this exist for Android? [RxJava](https://github.com/ReactiveX/RxJava) * Where is all of this going, what is the future, what about reactive architectures, how do you design entire apps this way? [Cycle.js](https://github.com/cyclejs/cycle-core) - this is javascript, but [RxJS](https://github.com/Reactive-Extensions/RxJS) is javascript version of Rx. ## Usage
Here's an example In Action
Define search for GitHub repositories ...
let searchResults = searchBar.rx.text.orEmpty
    .throttle(0.3, scheduler: MainScheduler.instance)
    .distinctUntilChanged()
    .flatMapLatest { query -> Observable<[Repository]> in
        if query.isEmpty {
            return .just([])
        }
        return searchGitHub(query)
            .catchErrorJustReturn([])
    }
    .observeOn(MainScheduler.instance)
... then bind the results to your tableview
searchResults
    .bind(to: tableView.rx.items(cellIdentifier: "Cell")) {
        (index, repository: Repository, cell) in
        cell.textLabel?.text = repository.name
        cell.detailTextLabel?.text = repository.url
    }
    .disposed(by: disposeBag)
## Requirements * Xcode 8.0 * Swift 3.0 * Swift 2.3 ([use `rxswift-2.0` branch](https://github.com/ReactiveX/RxSwift/tree/rxswift-2.0) instead) ## Installation Rx doesn't contain any external dependencies. These are currently the supported options: ### Manual Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build everything and run the sample app ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) **Tested with `pod --version`: `1.1.1`** ```ruby # Podfile use_frameworks! target 'YOUR_TARGET_NAME' do pod 'RxSwift', '~> 3.0' pod 'RxCocoa', '~> 3.0' end # RxTests and RxBlocking make the most sense in the context of unit/integration tests target 'YOUR_TESTING_TARGET' do pod 'RxBlocking', '~> 3.0' pod 'RxTest', '~> 3.0' end ``` Replace `YOUR_TARGET_NAME` and then, in the `Podfile` directory, type: ```bash $ pod install ``` ### [Carthage](https://github.com/Carthage/Carthage) **Tested with `carthage version`: `0.18.1`** Add this to `Cartfile` ``` github "ReactiveX/RxSwift" ~> 3.0 ``` ```bash $ carthage update ``` ### [Swift Package Manager](https://github.com/apple/swift-package-manager) **Tested with `swift build --version`: `3.0.0 (swiftpm-19)`** Create a `Package.swift` file. ```swift import PackageDescription let package = Package( name: "RxTestProject", targets: [], dependencies: [ .Package(url: "https://github.com/ReactiveX/RxSwift.git", majorVersion: 3) ] ) ``` ```bash $ swift build ``` To build or test a module with RxTest dependency, set `TEST=1`. ([RxSwift >= 3.4.2](https://github.com/ReactiveX/RxSwift/releases/tag/3.4.2)) ```bash $ TEST=1 swift test ``` ### Manually using git submodules * Add RxSwift as a submodule ```bash $ git submodule add git@github.com:ReactiveX/RxSwift.git ``` * Drag `Rx.xcodeproj` into Project Navigator * Go to `Project > Targets > Build Phases > Link Binary With Libraries`, click `+` and select `RxSwift-[Platform]` and `RxCocoa-[Platform]` targets ## References * [http://reactivex.io/](http://reactivex.io/) * [Reactive Extensions GitHub (GitHub)](https://github.com/Reactive-Extensions) * [RxSwift RayWenderlich.com Book](https://store.raywenderlich.com/products/rxswift) * [Boxue.io RxSwift Online Course](https://boxueio.com/series/rxswift-101) (Chinese 🇨🇳) * [Erik Meijer (Wikipedia)](http://en.wikipedia.org/wiki/Erik_Meijer_%28computer_scientist%29) * [Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx) (video)](https://youtu.be/looJcaeboBY) * [Reactive Programming Overview (Jafar Husain from Netflix)](https://www.youtube.com/watch?v=dwP1TNXE6fc) * [Subject/Observer is Dual to Iterator (paper)](http://csl.stanford.edu/~christos/pldi2010.fit/meijer.duality.pdf) * [Rx standard sequence operators visualized (visualization tool)](http://rxmarbles.com/) * [Haskell](https://www.haskell.org/) ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/Binder.swift ================================================ // // Binder.swift // RxCocoa // // Created by Krunoslav Zaher on 9/17/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // import RxSwift /** Observer that enforces interface binding rules: * can't bind errors (in debug builds binding of errors causes `fatalError` in release builds errors are being logged) * ensures binding is performed on a specific scheduler `Binder` doesn't retain target and in case target is released, element isn't bound. By default it binds elements on main scheduler. */ public struct Binder: ObserverType { public typealias E = Value private let _binding: (Event) -> () /// Initializes `Binder` /// /// - parameter target: Target object. /// - parameter scheduler: Scheduler used to bind the events. /// - parameter binding: Binding logic. public init(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping (Target, Value) -> ()) { weak var weakTarget = target _binding = { event in switch event { case .next(let element): _ = scheduler.schedule(element) { element in if let target = weakTarget { binding(target, element) } return Disposables.create() } case .error(let error): bindingError(error) case .completed: break } } } /// Binds next element to owner view as described in `binding`. public func on(_ event: Event) { _binding(event) } /// Erases type of observer. /// /// - returns: type erased observer. public func asObserver() -> AnyObserver { return AnyObserver(eventHandler: on) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/ControlTarget.swift ================================================ // // ControlTarget.swift // RxCocoa // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) || os(macOS) #if !RX_NO_MODULE import RxSwift #endif #if os(iOS) || os(tvOS) import UIKit typealias Control = UIKit.UIControl typealias ControlEvents = UIKit.UIControlEvents #elseif os(macOS) import Cocoa typealias Control = Cocoa.NSControl #endif // This should be only used from `MainScheduler` final class ControlTarget: RxTarget { typealias Callback = (Control) -> Void let selector: Selector = #selector(ControlTarget.eventHandler(_:)) weak var control: Control? #if os(iOS) || os(tvOS) let controlEvents: UIControlEvents #endif var callback: Callback? #if os(iOS) || os(tvOS) init(control: Control, controlEvents: UIControlEvents, callback: @escaping Callback) { MainScheduler.ensureExecutingOnScheduler() self.control = control self.controlEvents = controlEvents self.callback = callback super.init() control.addTarget(self, action: selector, for: controlEvents) let method = self.method(for: selector) if method == nil { rxFatalError("Can't find method") } } #elseif os(macOS) init(control: Control, callback: @escaping Callback) { MainScheduler.ensureExecutingOnScheduler() self.control = control self.callback = callback super.init() control.target = self control.action = selector let method = self.method(for: selector) if method == nil { rxFatalError("Can't find method") } } #endif @objc func eventHandler(_ sender: Control!) { if let callback = self.callback, let control = self.control { callback(control) } } override func dispose() { super.dispose() #if os(iOS) || os(tvOS) self.control?.removeTarget(self, action: self.selector, for: self.controlEvents) #elseif os(macOS) self.control?.target = nil self.control?.action = nil #endif self.callback = nil } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/DelegateProxy.swift ================================================ // // DelegateProxy.swift // RxCocoa // // Created by Krunoslav Zaher on 6/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !os(Linux) #if !RX_NO_MODULE import RxSwift #if SWIFT_PACKAGE && !os(Linux) import RxCocoaRuntime #endif #endif /// Base class for `DelegateProxyType` protocol. /// /// This implementation is not thread safe and can be used only from one thread (Main thread). open class DelegateProxy: _RXDelegateProxy { public typealias ParentObject = P public typealias Delegate = D private var _sentMessageForSelector = [Selector: MessageDispatcher]() private var _methodInvokedForSelector = [Selector: MessageDispatcher]() /// Parent object associated with delegate proxy. private weak private(set) var _parentObject: ParentObject? fileprivate let _currentDelegateFor: (ParentObject) -> Delegate? fileprivate let _setCurrentDelegateTo: (Delegate?, ParentObject) -> () /// Initializes new instance. /// /// - parameter parentObject: Optional parent object that owns `DelegateProxy` as associated object. public init(parentObject: ParentObject, delegateProxy: Proxy.Type) where Proxy: DelegateProxy, Proxy.ParentObject == ParentObject, Proxy.Delegate == Delegate { self._parentObject = parentObject self._currentDelegateFor = delegateProxy.currentDelegate(for:) self._setCurrentDelegateTo = delegateProxy.setCurrentDelegate(_:to:) MainScheduler.ensureExecutingOnScheduler() #if TRACE_RESOURCES _ = Resources.incrementTotal() #endif super.init() } /** Returns observable sequence of invocations of delegate methods. Elements are sent *before method is invoked*. Only methods that have `void` return value can be observed using this method because those methods are used as a notification mechanism. It doesn't matter if they are optional or not. Observing is performed by installing a hidden associated `PublishSubject` that is used to dispatch messages to observers. Delegate methods that have non `void` return value can't be observed directly using this method because: * those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism * there is no sensible automatic way to determine a default return value In case observing of delegate methods that have return type is required, it can be done by manually installing a `PublishSubject` or `BehaviorSubject` and implementing delegate method. e.g. // delegate proxy part (RxScrollViewDelegateProxy) let internalSubject = PublishSubject public func requiredDelegateMethod(scrollView: UIScrollView, arg1: CGPoint) -> Bool { internalSubject.on(.next(arg1)) return self._forwardToDelegate?.requiredDelegateMethod?(scrollView, arg1: arg1) ?? defaultReturnValue } .... // reactive property implementation in a real class (`UIScrollView`) public var property: Observable { let proxy = RxScrollViewDelegateProxy.proxy(for: base) return proxy.internalSubject.asObservable() } **In case calling this method prints "Delegate proxy is already implementing `\(selector)`, a more performant way of registering might exist.", that means that manual observing method is required analog to the example above because delegate method has already been implemented.** - parameter selector: Selector used to filter observed invocations of delegate methods. - returns: Observable sequence of arguments passed to `selector` method. */ open func sentMessage(_ selector: Selector) -> Observable<[Any]> { MainScheduler.ensureExecutingOnScheduler() checkSelectorIsObservable(selector) let subject = _sentMessageForSelector[selector] if let subject = subject { return subject.asObservable() } else { let subject = MessageDispatcher(delegateProxy: self) _sentMessageForSelector[selector] = subject return subject.asObservable() } } /** Returns observable sequence of invoked delegate methods. Elements are sent *after method is invoked*. Only methods that have `void` return value can be observed using this method because those methods are used as a notification mechanism. It doesn't matter if they are optional or not. Observing is performed by installing a hidden associated `PublishSubject` that is used to dispatch messages to observers. Delegate methods that have non `void` return value can't be observed directly using this method because: * those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism * there is no sensible automatic way to determine a default return value In case observing of delegate methods that have return type is required, it can be done by manually installing a `PublishSubject` or `BehaviorSubject` and implementing delegate method. e.g. // delegate proxy part (RxScrollViewDelegateProxy) let internalSubject = PublishSubject public func requiredDelegateMethod(scrollView: UIScrollView, arg1: CGPoint) -> Bool { internalSubject.on(.next(arg1)) return self._forwardToDelegate?.requiredDelegateMethod?(scrollView, arg1: arg1) ?? defaultReturnValue } .... // reactive property implementation in a real class (`UIScrollView`) public var property: Observable { let proxy = RxScrollViewDelegateProxy.proxy(for: base) return proxy.internalSubject.asObservable() } **In case calling this method prints "Delegate proxy is already implementing `\(selector)`, a more performant way of registering might exist.", that means that manual observing method is required analog to the example above because delegate method has already been implemented.** - parameter selector: Selector used to filter observed invocations of delegate methods. - returns: Observable sequence of arguments passed to `selector` method. */ open func methodInvoked(_ selector: Selector) -> Observable<[Any]> { MainScheduler.ensureExecutingOnScheduler() checkSelectorIsObservable(selector) let subject = _methodInvokedForSelector[selector] if let subject = subject { return subject.asObservable() } else { let subject = MessageDispatcher(delegateProxy: self) _methodInvokedForSelector[selector] = subject return subject.asObservable() } } private func checkSelectorIsObservable(_ selector: Selector) { MainScheduler.ensureExecutingOnScheduler() if hasWiredImplementation(for: selector) { print("Delegate proxy is already implementing `\(selector)`, a more performant way of registering might exist.") return } guard ((self.forwardToDelegate() as? NSObject)?.responds(to: selector) ?? false) || voidDelegateMethodsContain(selector) else { rxFatalError("This class doesn't respond to selector \(selector)") } } // proxy open override func _sentMessage(_ selector: Selector, withArguments arguments: [Any]) { _sentMessageForSelector[selector]?.on(.next(arguments)) } open override func _methodInvoked(_ selector: Selector, withArguments arguments: [Any]) { _methodInvokedForSelector[selector]?.on(.next(arguments)) } /// Returns reference of normal delegate that receives all forwarded messages /// through `self`. /// /// - returns: Value of reference if set or nil. open func forwardToDelegate() -> Delegate? { return castOptionalOrFatalError(self._forwardToDelegate) } /// Sets reference of normal delegate that receives all forwarded messages /// through `self`. /// /// - parameter forwardToDelegate: Reference of delegate that receives all messages through `self`. /// - parameter retainDelegate: Should `self` retain `forwardToDelegate`. open func setForwardToDelegate(_ delegate: Delegate?, retainDelegate: Bool) { #if DEBUG // 4.0 all configurations MainScheduler.ensureExecutingOnScheduler() #endif self._setForwardToDelegate(delegate, retainDelegate: retainDelegate) self.reset() } private func hasObservers(selector: Selector) -> Bool { return (_sentMessageForSelector[selector]?.hasObservers ?? false) || (_methodInvokedForSelector[selector]?.hasObservers ?? false) } override open func responds(to aSelector: Selector!) -> Bool { return super.responds(to: aSelector) || (self._forwardToDelegate?.responds(to: aSelector) ?? false) || (self.voidDelegateMethodsContain(aSelector) && self.hasObservers(selector: aSelector)) } fileprivate func reset() { guard let parentObject = self._parentObject else { return } let maybeCurrentDelegate = _currentDelegateFor(parentObject) if maybeCurrentDelegate === self { _setCurrentDelegateTo(nil, parentObject) _setCurrentDelegateTo(castOrFatalError(self), parentObject) } } deinit { for v in _sentMessageForSelector.values { v.on(.completed) } for v in _methodInvokedForSelector.values { v.on(.completed) } #if TRACE_RESOURCES _ = Resources.decrementTotal() #endif } } fileprivate let mainScheduler = MainScheduler() fileprivate final class MessageDispatcher { private let dispatcher: PublishSubject<[Any]> private let result: Observable<[Any]> init(delegateProxy _delegateProxy: DelegateProxy) { weak var weakDelegateProxy = _delegateProxy let dispatcher = PublishSubject<[Any]>() self.dispatcher = dispatcher self.result = dispatcher .do(onSubscribed: { weakDelegateProxy?.reset() }, onDispose: { weakDelegateProxy?.reset() }) .share() .subscribeOn(mainScheduler) } var on: (Event<[Any]>) -> () { return self.dispatcher.on } var hasObservers: Bool { return self.dispatcher.hasObservers } func asObservable() -> Observable<[Any]> { return self.result } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/DelegateProxyType.swift ================================================ // // DelegateProxyType.swift // RxCocoa // // Created by Krunoslav Zaher on 6/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !os(Linux) import func Foundation.objc_getAssociatedObject import func Foundation.objc_setAssociatedObject #if !RX_NO_MODULE import RxSwift #endif /** `DelegateProxyType` protocol enables using both normal delegates and Rx observable sequences with views that can have only one delegate/datasource registered. `Proxies` store information about observers, subscriptions and delegates for specific views. Type implementing `DelegateProxyType` should never be initialized directly. To fetch initialized instance of type implementing `DelegateProxyType`, `proxy` method should be used. This is more or less how it works. +-------------------------------------------+ | | | UIView subclass (UIScrollView) | | | +-----------+-------------------------------+ | | Delegate | | +-----------v-------------------------------+ | | | Delegate proxy : DelegateProxyType +-----+----> Observable | , UIScrollViewDelegate | | +-----------+-------------------------------+ +----> Observable | | | +----> Observable | | | forwards events | | to custom delegate | | v +-----------v-------------------------------+ | | | Custom delegate (UIScrollViewDelegate) | | | +-------------------------------------------+ Since RxCocoa needs to automagically create those Proxys and because views that have delegates can be hierarchical UITableView : UIScrollView : UIView .. and corresponding delegates are also hierarchical UITableViewDelegate : UIScrollViewDelegate : NSObject ... this mechanism can be extended by using the following snippet in `registerKnownImplementations` or in some other part of your app that executes before using `rx.*` (e.g. appDidFinishLaunching). RxScrollViewDelegateProxy.register { RxTableViewDelegateProxy(parentObject: $0) } */ public protocol DelegateProxyType: class { associatedtype ParentObject: AnyObject associatedtype Delegate: AnyObject /// It is require that enumerate call `register` of the extended DelegateProxy subclasses here. static func registerKnownImplementations() /// Unique identifier for delegate static var identifier: UnsafeRawPointer { get } /// Returns designated delegate property for object. /// /// Objects can have multiple delegate properties. /// /// Each delegate property needs to have it's own type implementing `DelegateProxyType`. /// /// It's abstract method. /// /// - parameter object: Object that has delegate property. /// - returns: Value of delegate property. static func currentDelegate(for object: ParentObject) -> Delegate? /// Sets designated delegate property for object. /// /// Objects can have multiple delegate properties. /// /// Each delegate property needs to have it's own type implementing `DelegateProxyType`. /// /// It's abstract method. /// /// - parameter toObject: Object that has delegate property. /// - parameter delegate: Delegate value. static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) /// Returns reference of normal delegate that receives all forwarded messages /// through `self`. /// /// - returns: Value of reference if set or nil. func forwardToDelegate() -> Delegate? /// Sets reference of normal delegate that receives all forwarded messages /// through `self`. /// /// - parameter forwardToDelegate: Reference of delegate that receives all messages through `self`. /// - parameter retainDelegate: Should `self` retain `forwardToDelegate`. func setForwardToDelegate(_ forwardToDelegate: Delegate?, retainDelegate: Bool) } // default implementations extension DelegateProxyType { /// Unique identifier for delegate public static var identifier: UnsafeRawPointer { let delegateIdentifier = ObjectIdentifier(Delegate.self) let integerIdentifier = Int(bitPattern: delegateIdentifier) return UnsafeRawPointer(bitPattern: integerIdentifier)! } } extension DelegateProxyType { /// Store DelegateProxy subclass to factory. /// When make 'Rx*DelegateProxy' subclass, call 'Rx*DelegateProxySubclass.register(for:_)' 1 time, or use it in DelegateProxyFactory /// 'Rx*DelegateProxy' can have one subclass implementation per concrete ParentObject type. /// Should call it from concrete DelegateProxy type, not generic. public static func register(make: @escaping (Parent) -> Self) { self.factory.extend(make: make) } /// Creates new proxy for target object. /// Should not call this function directory, use 'DelegateProxy.proxy(for:)' public static func createProxy(for object: AnyObject) -> Self { return castOrFatalError(factory.createProxy(for: object)) } /// Returns existing proxy for object or installs new instance of delegate proxy. /// /// - parameter object: Target object on which to install delegate proxy. /// - returns: Installed instance of delegate proxy. /// /// /// extension Reactive where Base: UISearchBar { /// /// public var delegate: DelegateProxy { /// return RxSearchBarDelegateProxy.proxy(for: base) /// } /// /// public var text: ControlProperty { /// let source: Observable = self.delegate.observe(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) /// ... /// } /// } public static func proxy(for object: ParentObject) -> Self { MainScheduler.ensureExecutingOnScheduler() let maybeProxy = self.assignedProxy(for: object) // Type is ideally be `(Self & Delegate)`, but Swift 3.0 doesn't support it. let proxy: Delegate if let existingProxy = maybeProxy { proxy = existingProxy } else { proxy = castOrFatalError(self.createProxy(for: object)) self.assignProxy(proxy, toObject: object) assert(self.assignedProxy(for: object) === proxy) } let currentDelegate = self.currentDelegate(for: object) let delegateProxy: Self = castOrFatalError(proxy) if currentDelegate !== delegateProxy { delegateProxy.setForwardToDelegate(currentDelegate, retainDelegate: false) assert(delegateProxy.forwardToDelegate() === currentDelegate) self.setCurrentDelegate(proxy, to: object) assert(self.currentDelegate(for: object) === proxy) assert(delegateProxy.forwardToDelegate() === currentDelegate) } return delegateProxy } /// Sets forward delegate for `DelegateProxyType` associated with a specific object and return disposable that can be used to unset the forward to delegate. /// Using this method will also make sure that potential original object cached selectors are cleared and will report any accidental forward delegate mutations. /// /// - parameter forwardDelegate: Delegate object to set. /// - parameter retainDelegate: Retain `forwardDelegate` while it's being set. /// - parameter onProxyForObject: Object that has `delegate` property. /// - returns: Disposable object that can be used to clear forward delegate. public static func installForwardDelegate(_ forwardDelegate: Delegate, retainDelegate: Bool, onProxyForObject object: ParentObject) -> Disposable { weak var weakForwardDelegate: AnyObject? = forwardDelegate let proxy = self.proxy(for: object) assert(proxy.forwardToDelegate() === nil, "This is a feature to warn you that there is already a delegate (or data source) set somewhere previously. The action you are trying to perform will clear that delegate (data source) and that means that some of your features that depend on that delegate (data source) being set will likely stop working.\n" + "If you are ok with this, try to set delegate (data source) to `nil` in front of this operation.\n" + " This is the source object value: \(object)\n" + " This this the original delegate (data source) value: \(proxy.forwardToDelegate()!)\n" + "Hint: Maybe delegate was already set in xib or storyboard and now it's being overwritten in code.\n") proxy.setForwardToDelegate(forwardDelegate, retainDelegate: retainDelegate) return Disposables.create { MainScheduler.ensureExecutingOnScheduler() let delegate: AnyObject? = weakForwardDelegate assert(delegate == nil || proxy.forwardToDelegate() === delegate, "Delegate was changed from time it was first set. Current \(String(describing: proxy.forwardToDelegate())), and it should have been \(proxy)") proxy.setForwardToDelegate(nil, retainDelegate: retainDelegate) } } } // fileprivate extensions extension DelegateProxyType { fileprivate static var factory: DelegateProxyFactory { return DelegateProxyFactory.sharedFactory(for: self) } fileprivate static func assignedProxy(for object: ParentObject) -> Delegate? { let maybeDelegate = objc_getAssociatedObject(object, self.identifier) return castOptionalOrFatalError(maybeDelegate.map { $0 as AnyObject }) } fileprivate static func assignProxy(_ proxy: Delegate, toObject object: ParentObject) { objc_setAssociatedObject(object, self.identifier, proxy, .OBJC_ASSOCIATION_RETAIN) } } /// Describes an object that has a delegate. public protocol HasDelegate: AnyObject { /// Delegate type associatedtype Delegate: AnyObject /// Delegate var delegate: Delegate? { get set } } extension DelegateProxyType where ParentObject: HasDelegate, Self.Delegate == ParentObject.Delegate { public static func currentDelegate(for object: ParentObject) -> Delegate? { return object.delegate } public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) { object.delegate = delegate } } /// Describes an object that has a data source. public protocol HasDataSource: AnyObject { /// Data source type associatedtype DataSource: AnyObject /// Data source var dataSource: DataSource? { get set } } extension DelegateProxyType where ParentObject: HasDataSource, Self.Delegate == ParentObject.DataSource { public static func currentDelegate(for object: ParentObject) -> Delegate? { return object.dataSource } public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) { object.dataSource = delegate } } #if os(iOS) || os(tvOS) import UIKit extension ObservableType { func subscribeProxyDataSource(ofObject object: DelegateProxy.ParentObject, dataSource: DelegateProxy.Delegate, retainDataSource: Bool, binding: @escaping (DelegateProxy, Event) -> Void) -> Disposable where DelegateProxy.ParentObject: UIView { let proxy = DelegateProxy.proxy(for: object) let unregisterDelegate = DelegateProxy.installForwardDelegate(dataSource, retainDelegate: retainDataSource, onProxyForObject: object) // this is needed to flush any delayed old state (https://github.com/RxSwiftCommunity/RxDataSources/pull/75) object.layoutIfNeeded() let subscription = self.asObservable() .observeOn(MainScheduler()) .catchError { error in bindingError(error) return Observable.empty() } // source can never end, otherwise it would release the subscriber, and deallocate the data source .concat(Observable.never()) .takeUntil(object.rx.deallocated) .subscribe { [weak object] (event: Event) in if let object = object { assert(proxy === DelegateProxy.currentDelegate(for: object), "Proxy changed from the time it was first set.\nOriginal: \(proxy)\nExisting: \(String(describing: DelegateProxy.currentDelegate(for: object)))") } binding(proxy, event) switch event { case .error(let error): bindingError(error) unregisterDelegate.dispose() case .completed: unregisterDelegate.dispose() default: break } } return Disposables.create { [weak object] in subscription.dispose() object?.layoutIfNeeded() unregisterDelegate.dispose() } } } #endif /** To add delegate proxy subclasses call `DelegateProxySubclass.register()` in `registerKnownImplementations` or in some other part of your app that executes before using `rx.*` (e.g. appDidFinishLaunching). class RxScrollViewDelegateProxy: DelegateProxy { public static func registerKnownImplementations() { self.register { RxTableViewDelegateProxy(parentObject: $0) } } ... */ private class DelegateProxyFactory { private static var _sharedFactories: [UnsafeRawPointer: DelegateProxyFactory] = [:] fileprivate static func sharedFactory(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory { MainScheduler.ensureExecutingOnScheduler() let identifier = DelegateProxy.identifier if let factory = _sharedFactories[identifier] { return factory } let factory = DelegateProxyFactory(for: proxyType) _sharedFactories[identifier] = factory DelegateProxy.registerKnownImplementations() return factory } private var _factories: [ObjectIdentifier: ((AnyObject) -> AnyObject)] private var _delegateProxyType: Any.Type private var _identifier: UnsafeRawPointer private init(for proxyType: DelegateProxy.Type) { _factories = [:] _delegateProxyType = proxyType _identifier = proxyType.identifier } fileprivate func extend(make: @escaping (ParentObject) -> DelegateProxy) { MainScheduler.ensureExecutingOnScheduler() precondition(_identifier == DelegateProxy.identifier, "Delegate proxy has inconsistent identifier") precondition((DelegateProxy.self as? DelegateProxy.Delegate) != nil, "DelegateProxy subclass should be as a Delegate") guard _factories[ObjectIdentifier(ParentObject.self)] == nil else { rxFatalError("The factory of \(ParentObject.self) is duplicated. DelegateProxy is not allowed of duplicated base object type.") } _factories[ObjectIdentifier(ParentObject.self)] = { make(castOrFatalError($0)) } } fileprivate func createProxy(for object: AnyObject) -> AnyObject { MainScheduler.ensureExecutingOnScheduler() var maybeMirror: Mirror? = Mirror(reflecting: object) while let mirror = maybeMirror { if let factory = _factories[ObjectIdentifier(mirror.subjectType)] { return factory(object) } maybeMirror = mirror.superclassMirror } rxFatalError("DelegateProxy has no factory of \(object). Implement DelegateProxy subclass for \(object) first.") } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/NSLayoutConstraint+Rx.swift ================================================ // // NSLayoutConstraint+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 12/6/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !os(Linux) #if os(macOS) import Cocoa #else import UIKit #endif #if !RX_NO_MODULE import RxSwift #endif #if os(iOS) || os(macOS) || os(tvOS) extension Reactive where Base: NSLayoutConstraint { /// Bindable sink for `constant` property. public var constant: Binder { return Binder(self.base) { constraint, constant in constraint.constant = constant } } /// Bindable sink for `active` property. @available(iOS 8, OSX 10.10, *) public var active: Binder { return Binder(self.base) { constraint, value in constraint.isActive = value } } } #endif #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/Observable+Bind.swift ================================================ // // Observable+Bind.swift // RxCocoa // // Created by Krunoslav Zaher on 8/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension ObservableType { /** Creates new subscription and sends elements to observer. In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables writing more consistent binding code. - parameter to: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer. */ public func bind(to observer: O) -> Disposable where O.E == E { return self.subscribe(observer) } /** Creates new subscription and sends elements to observer. In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables writing more consistent binding code. - parameter to: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer. */ public func bind(to observer: O) -> Disposable where O.E == E? { return self.map { $0 }.subscribe(observer) } /** Creates new subscription and sends elements to variable. In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - parameter to: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ public func bind(to variable: Variable) -> Disposable { return subscribe { e in switch e { case let .next(element): variable.value = element case let .error(error): let error = "Binding error to variable: \(error)" #if DEBUG rxFatalError(error) #else print(error) #endif case .completed: break } } } /** Creates new subscription and sends elements to variable. In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - parameter to: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ public func bind(to variable: Variable) -> Disposable { return self.map { $0 as E? }.bind(to: variable) } /** Subscribes to observable sequence using custom binder function. - parameter to: Function used to bind elements from `self`. - returns: Object representing subscription. */ public func bind(to binder: (Self) -> R) -> R { return binder(self) } /** Subscribes to observable sequence using custom binder function and final parameter passed to binder function after `self` is passed. public func bind(to binder: Self -> R1 -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) } - parameter to: Function used to bind elements from `self`. - parameter curriedArgument: Final argument passed to `binder` to finish binding process. - returns: Object representing subscription. */ public func bind(to binder: (Self) -> (R1) -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) } /** Subscribes an element handler to an observable sequence. In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ public func bind(onNext: @escaping (E) -> Void) -> Disposable { return subscribe(onNext: onNext, onError: { error in let error = "Binding error: \(error)" #if DEBUG rxFatalError(error) #else print(error) #endif }) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/RxCocoaObjCRuntimeError+Extensions.swift ================================================ // // RxCocoaObjCRuntimeError+Extensions.swift // RxCocoa // // Created by Krunoslav Zaher on 10/9/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE #if SWIFT_PACKAGE && !DISABLE_SWIZZLING && !os(Linux) import RxCocoaRuntime #endif #endif #if !DISABLE_SWIZZLING && !os(Linux) /// RxCocoa ObjC runtime interception mechanism. public enum RxCocoaInterceptionMechanism { /// Unknown message interception mechanism. case unknown /// Key value observing interception mechanism. case kvo } /// RxCocoa ObjC runtime modification errors. public enum RxCocoaObjCRuntimeError : Swift.Error , CustomDebugStringConvertible { /// Unknown error has occurred. case unknown(target: AnyObject) /** If the object is reporting a different class then it's real class, that means that there is probably already some interception mechanism in place or something weird is happening. The most common case when this would happen is when using a combination of KVO (`observe`) and `sentMessage`. This error is easily resolved by just using `sentMessage` observing before `observe`. The reason why the other way around could create issues is because KVO will unregister it's interceptor class and restore original class. Unfortunately that will happen no matter was there another interceptor subclass registered in hierarchy or not. Failure scenario: * KVO sets class to be `__KVO__OriginalClass` (subclass of `OriginalClass`) * `sentMessage` sets object class to be `_RX_namespace___KVO__OriginalClass` (subclass of `__KVO__OriginalClass`) * then unobserving with KVO will restore class to be `OriginalClass` -> failure point (possibly a bug in KVO) The reason why changing order of observing works is because any interception method on unregistration should return object's original real class (if that doesn't happen then it's really easy to argue that's a bug in that interception mechanism). This library won't remove registered interceptor even if there aren't any observers left because it's highly unlikely it would have any benefit in real world use cases, and it's even more dangerous. */ case objectMessagesAlreadyBeingIntercepted(target: AnyObject, interceptionMechanism: RxCocoaInterceptionMechanism) /// Trying to observe messages for selector that isn't implemented. case selectorNotImplemented(target: AnyObject) /// Core Foundation classes are usually toll free bridged. Those classes crash the program in case /// `object_setClass` is performed on them. /// /// There is a possibility to just swizzle methods on original object, but since those won't be usual use /// cases for this library, then an error will just be reported for now. case cantInterceptCoreFoundationTollFreeBridgedObjects(target: AnyObject) /// Two libraries have simultaneously tried to modify ObjC runtime and that was detected. This can only /// happen in scenarios where multiple interception libraries are used. /// /// To synchronize other libraries intercepting messages for an object, use `synchronized` on target object and /// it's meta-class. case threadingCollisionWithOtherInterceptionMechanism(target: AnyObject) /// For some reason saving original method implementation under RX namespace failed. case savingOriginalForwardingMethodFailed(target: AnyObject) /// Intercepting a sent message by replacing a method implementation with `_objc_msgForward` failed for some reason. case replacingMethodWithForwardingImplementation(target: AnyObject) /// Attempt to intercept one of the performance sensitive methods: /// * class /// * respondsToSelector: /// * methodSignatureForSelector: /// * forwardingTargetForSelector: case observingPerformanceSensitiveMessages(target: AnyObject) /// Message implementation has unsupported return type (for example large struct). The reason why this is a error /// is because in some cases intercepting sent messages requires replacing implementation with `_objc_msgForward_stret` /// instead of `_objc_msgForward`. /// /// The unsupported cases should be fairly uncommon. case observingMessagesWithUnsupportedReturnType(target: AnyObject) } extension RxCocoaObjCRuntimeError { /// A textual representation of `self`, suitable for debugging. public var debugDescription: String { switch self { case let .unknown(target): return "Unknown error occurred.\nTarget: `\(target)`" case let .objectMessagesAlreadyBeingIntercepted(target, interceptionMechanism): let interceptionMechanismDescription = interceptionMechanism == .kvo ? "KVO" : "other interception mechanism" return "Collision between RxCocoa interception mechanism and \(interceptionMechanismDescription)." + " To resolve this conflict please use this interception mechanism first.\nTarget: \(target)" case let .selectorNotImplemented(target): return "Trying to observe messages for selector that isn't implemented.\nTarget: \(target)" case let .cantInterceptCoreFoundationTollFreeBridgedObjects(target): return "Interception of messages sent to Core Foundation isn't supported.\nTarget: \(target)" case let .threadingCollisionWithOtherInterceptionMechanism(target): return "Detected a conflict while modifying ObjC runtime.\nTarget: \(target)" case let .savingOriginalForwardingMethodFailed(target): return "Saving original method implementation failed.\nTarget: \(target)" case let .replacingMethodWithForwardingImplementation(target): return "Intercepting a sent message by replacing a method implementation with `_objc_msgForward` failed for some reason.\nTarget: \(target)" case let .observingPerformanceSensitiveMessages(target): return "Attempt to intercept one of the performance sensitive methods. \nTarget: \(target)" case let .observingMessagesWithUnsupportedReturnType(target): return "Attempt to intercept a method with unsupported return type. \nTarget: \(target)" } } } // MARK: Conversions `NSError` > `RxCocoaObjCRuntimeError` extension Error { func rxCocoaErrorForTarget(_ target: AnyObject) -> RxCocoaObjCRuntimeError { let error = self as NSError if error.domain == RXObjCRuntimeErrorDomain { let errorCode = RXObjCRuntimeError(rawValue: error.code) ?? .unknown switch errorCode { case .unknown: return .unknown(target: target) case .objectMessagesAlreadyBeingIntercepted: let isKVO = (error.userInfo[RXObjCRuntimeErrorIsKVOKey] as? NSNumber)?.boolValue ?? false return .objectMessagesAlreadyBeingIntercepted(target: target, interceptionMechanism: isKVO ? .kvo : .unknown) case .selectorNotImplemented: return .selectorNotImplemented(target: target) case .cantInterceptCoreFoundationTollFreeBridgedObjects: return .cantInterceptCoreFoundationTollFreeBridgedObjects(target: target) case .threadingCollisionWithOtherInterceptionMechanism: return .threadingCollisionWithOtherInterceptionMechanism(target: target) case .savingOriginalForwardingMethodFailed: return .savingOriginalForwardingMethodFailed(target: target) case .replacingMethodWithForwardingImplementation: return .replacingMethodWithForwardingImplementation(target: target) case .observingPerformanceSensitiveMessages: return .observingPerformanceSensitiveMessages(target: target) case .observingMessagesWithUnsupportedReturnType: return .observingMessagesWithUnsupportedReturnType(target: target) } } return RxCocoaObjCRuntimeError.unknown(target: target) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/RxTarget.swift ================================================ // // RxTarget.swift // RxCocoa // // Created by Krunoslav Zaher on 7/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import class Foundation.NSObject #if !RX_NO_MODULE import RxSwift #endif class RxTarget : NSObject , Disposable { private var retainSelf: RxTarget? override init() { super.init() self.retainSelf = self #if TRACE_RESOURCES _ = Resources.incrementTotal() #endif #if DEBUG MainScheduler.ensureExecutingOnScheduler() #endif } func dispose() { #if DEBUG MainScheduler.ensureExecutingOnScheduler() #endif self.retainSelf = nil } #if TRACE_RESOURCES deinit { _ = Resources.decrementTotal() } #endif } ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/SectionedViewDataSourceType.swift ================================================ // // SectionedViewDataSourceType.swift // RxCocoa // // Created by Krunoslav Zaher on 1/10/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import struct Foundation.IndexPath /// Data source with access to underlying sectioned model. public protocol SectionedViewDataSourceType { /// Returns model at index path. /// /// In case data source doesn't contain any sections when this method is being called, `RxCocoaError.ItemsNotYetBound(object: self)` is thrown. /// - parameter indexPath: Model index path /// - returns: Model at index path. func model(at indexPath: IndexPath) throws -> Any } ================================================ FILE: Pods/RxCocoa/RxCocoa/Common/TextInput.swift ================================================ // // TextInput.swift // RxCocoa // // Created by Krunoslav Zaher on 5/12/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif #if os(iOS) || os(tvOS) import UIKit /// Represents text input with reactive extensions. public struct TextInput { /// Base text input to extend. public let base: Base /// Reactive wrapper for `text` property. public let text: ControlProperty /// Initializes new text input. /// /// - parameter base: Base object. /// - parameter text: Textual control property. public init(base: Base, text: ControlProperty) { self.base = base self.text = text } } extension Reactive where Base: UITextField { /// Reactive text input. public var textInput: TextInput { return TextInput(base: base, text: self.text) } } extension Reactive where Base: UITextView { /// Reactive text input. public var textInput: TextInput { return TextInput(base: base, text: self.text) } } #endif #if os(macOS) import Cocoa /// Represents text input with reactive extensions. public struct TextInput { /// Base text input to extend. public let base: Base /// Reactive wrapper for `text` property. public let text: ControlProperty /// Initializes new text input. /// /// - parameter base: Base object. /// - parameter text: Textual control property. public init(base: Base, text: ControlProperty) { self.base = base self.text = text } } extension Reactive where Base: NSTextField, Base: NSTextInputClient { /// Reactive text input. public var textInput: TextInput { return TextInput(base: base, text: self.text) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Deprecated.swift ================================================ // // Deprecated.swift // RxCocoa // // Created by Krunoslav Zaher on 3/19/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif import Dispatch extension ObservableType { /** Creates new subscription and sends elements to observer. In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables writing more consistent binding code. - parameter observer: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer. */ @available(*, deprecated, renamed: "bind(to:)") public func bindTo(_ observer: O) -> Disposable where O.E == E { return self.subscribe(observer) } /** Creates new subscription and sends elements to observer. In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables writing more consistent binding code. - parameter observer: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer. */ @available(*, deprecated, renamed: "bind(to:)") public func bindTo(_ observer: O) -> Disposable where O.E == E? { return self.map { $0 }.subscribe(observer) } /** Creates new subscription and sends elements to variable. In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - parameter variable: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ @available(*, deprecated, renamed: "bind(to:)") public func bindTo(_ variable: Variable) -> Disposable { return subscribe { e in switch e { case let .next(element): variable.value = element case let .error(error): let error = "Binding error to variable: \(error)" #if DEBUG rxFatalError(error) #else print(error) #endif case .completed: break } } } /** Creates new subscription and sends elements to variable. In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - parameter variable: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer. */ @available(*, deprecated, renamed: "bind(to:)") public func bindTo(_ variable: Variable) -> Disposable { return self.map { $0 as E? }.bindTo(variable) } /** Subscribes to observable sequence using custom binder function. - parameter binder: Function used to bind elements from `self`. - returns: Object representing subscription. */ @available(*, deprecated, renamed: "bind(to:)") public func bindTo(_ binder: (Self) -> R) -> R { return binder(self) } /** Subscribes to observable sequence using custom binder function and final parameter passed to binder function after `self` is passed. public func bindTo(binder: Self -> R1 -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) } - parameter binder: Function used to bind elements from `self`. - parameter curriedArgument: Final argument passed to `binder` to finish binding process. - returns: Object representing subscription. */ @available(*, deprecated, renamed: "bind(to:)") public func bindTo(_ binder: (Self) -> (R1) -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) } /** Subscribes an element handler to an observable sequence. In case error occurs in debug mode, `fatalError` will be raised. In case error occurs in release mode, `error` will be logged. - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ @available(*, deprecated, renamed: "bind(onNext:)") public func bindNext(_ onNext: @escaping (E) -> Void) -> Disposable { return subscribe(onNext: onNext, onError: { error in let error = "Binding error: \(error)" #if DEBUG rxFatalError(error) #else print(error) #endif }) } } #if os(iOS) || os(tvOS) import UIKit extension NSTextStorage { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxTextStorageDelegateProxy { fatalError() } } extension UIScrollView { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxScrollViewDelegateProxy { fatalError() } } extension UICollectionView { @available(*, unavailable, message: "createRxDataSourceProxy is now unavailable, check DelegateProxyFactory") public func createRxDataSourceProxy() -> RxCollectionViewDataSourceProxy { fatalError() } } extension UITableView { @available(*, unavailable, message: "createRxDataSourceProxy is now unavailable, check DelegateProxyFactory") public func createRxDataSourceProxy() -> RxTableViewDataSourceProxy { fatalError() } } extension UINavigationBar { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxNavigationControllerDelegateProxy { fatalError() } } extension UINavigationController { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxNavigationControllerDelegateProxy { fatalError() } } extension UITabBar { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxTabBarDelegateProxy { fatalError() } } extension UITabBarController { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxTabBarControllerDelegateProxy { fatalError() } } extension UISearchBar { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxSearchBarDelegateProxy { fatalError() } } #endif #if os(iOS) extension UISearchController { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxSearchControllerDelegateProxy { fatalError() } } extension UIPickerView { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxPickerViewDelegateProxy { fatalError() } @available(*, unavailable, message: "createRxDataSourceProxy is now unavailable, check DelegateProxyFactory") public func createRxDataSourceProxy() -> RxPickerViewDataSourceProxy { fatalError() } } extension UIWebView { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxWebViewDelegateProxy { fatalError() } } #endif #if os(macOS) import Cocoa extension NSTextField { @available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory") public func createRxDelegateProxy() -> RxTextFieldDelegateProxy { fatalError() } } #endif /** This method can be used in unit tests to ensure that driver is using mock schedulers instead of main schedulers. **This shouldn't be used in normal release builds.** */ @available(*, deprecated, renamed: "SharingScheduler.mock(scheduler:action:)") public func driveOnScheduler(_ scheduler: SchedulerType, action: () -> ()) { SharingScheduler.mock(scheduler: scheduler, action: action) } extension Variable { /// Converts `Variable` to `SharedSequence` unit. /// /// - returns: Observable sequence. @available(*, deprecated, renamed: "asDriver()") public func asSharedSequence(strategy: SharingStrategy.Type = SharingStrategy.self) -> SharedSequence { let source = self.asObservable() .observeOn(SharingStrategy.scheduler) return SharedSequence(source) } } #if !os(Linux) extension DelegateProxy { @available(*, unavailable, renamed: "assignedProxy(for:)") public static func assignedProxyFor(_ object: ParentObject) -> Delegate? { fatalError() } @available(*, unavailable, renamed: "currentDelegate(for:)") public static func currentDelegateFor(_ object: ParentObject) -> Delegate? { fatalError() } } #endif /** Observer that enforces interface binding rules: * can't bind errors (in debug builds binding of errors causes `fatalError` in release builds errors are being logged) * ensures binding is performed on main thread `UIBindingObserver` doesn't retain target interface and in case owned interface element is released, element isn't bound. In case event binding is attempted from non main dispatch queue, event binding will be dispatched async to main dispatch queue. */ @available(*, deprecated, renamed: "Binder") public final class UIBindingObserver : ObserverType where UIElementType: AnyObject { public typealias E = Value weak var UIElement: UIElementType? let binding: (UIElementType, Value) -> Void /// Initializes `ViewBindingObserver` using @available(*, deprecated, renamed: "UIBinder.init(_:scheduler:binding:)") public init(UIElement: UIElementType, binding: @escaping (UIElementType, Value) -> Void) { self.UIElement = UIElement self.binding = binding } /// Binds next element to owner view as described in `binding`. public func on(_ event: Event) { if !DispatchQueue.isMain { DispatchQueue.main.async { self.on(event) } return } switch event { case .next(let element): if let view = self.UIElement { binding(view, element) } case .error(let error): bindingError(error) case .completed: break } } /// Erases type of observer. /// /// - returns: type erased observer. public func asObserver() -> AnyObserver { return AnyObserver(eventHandler: on) } } #if os(iOS) extension Reactive where Base: UIRefreshControl { /// Bindable sink for `beginRefreshing()`, `endRefreshing()` methods. @available(*, deprecated, renamed: "isRefreshing") public var refreshing: Binder { return self.isRefreshing } } #endif #if os(iOS) || os(tvOS) extension Reactive where Base: UIImageView { /// Bindable sink for `image` property. /// - parameter transitionType: Optional transition type while setting the image (kCATransitionFade, kCATransitionMoveIn, ...) @available(*, deprecated, renamed: "image") public func image(transitionType: String? = nil) -> Binder { return Binder(base) { imageView, image in if let transitionType = transitionType { if image != nil { let transition = CATransition() transition.duration = 0.25 transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) transition.type = transitionType imageView.layer.add(transition, forKey: kCATransition) } } else { imageView.layer.removeAllAnimations() } imageView.image = image } } } #endif #if os(macOS) extension Reactive where Base: NSImageView { /// Bindable sink for `image` property. /// /// - parameter transitionType: Optional transition type while setting the image (kCATransitionFade, kCATransitionMoveIn, ...) @available(*, deprecated, renamed: "image") public func image(transitionType: String? = nil) -> Binder { return Binder(self.base) { control, value in if let transitionType = transitionType { if value != nil { let transition = CATransition() transition.duration = 0.25 transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) transition.type = transitionType control.layer?.add(transition, forKey: kCATransition) } } else { control.layer?.removeAllAnimations() } control.image = value } } } #endif #if !RX_NO_MODULE import RxSwift #endif extension Variable { /// Converts `Variable` to `Driver` trait. /// /// - returns: Driving observable sequence. public func asDriver() -> Driver { let source = self.asObservable() .observeOn(DriverSharingStrategy.scheduler) return Driver(source) } } private let errorMessage = "`drive*` family of methods can be only called from `MainThread`.\n" + "This is required to ensure that the last replayed `Driver` element is delivered on `MainThread`.\n" extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { /** Creates new subscription and sends elements to variable. This method can be only called from `MainThread`. - parameter variable: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer from the variable. */ public func drive(_ variable: Variable) -> Disposable { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return drive(onNext: { e in variable.value = e }) } /** Creates new subscription and sends elements to variable. This method can be only called from `MainThread`. - parameter variable: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer from the variable. */ public func drive(_ variable: Variable) -> Disposable { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return drive(onNext: { e in variable.value = e }) } } extension ObservableConvertibleType { /** Converts anything convertible to `Observable` to `SharedSequence` unit. - parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence. - returns: Driving observable sequence. */ @available(*, deprecated, message: "Please use conversion methods to some SharedSequence specialization.") public func asSharedSequence(sharingStrategy: S.Type = S.self, onErrorJustReturn: E) -> SharedSequence { let source = self .asObservable() .observeOn(S.scheduler) .catchErrorJustReturn(onErrorJustReturn) return SharedSequence(source) } /** Converts anything convertible to `Observable` to `SharedSequence` unit. - parameter onErrorDriveWith: SharedSequence that provides elements of the sequence in case of error. - returns: Driving observable sequence. */ @available(*, deprecated, message: "Please use conversion methods to some SharedSequence specialization.") public func asSharedSequence(sharingStrategy: S.Type = S.self, onErrorDriveWith: SharedSequence) -> SharedSequence { let source = self .asObservable() .observeOn(S.scheduler) .catchError { _ in onErrorDriveWith.asObservable() } return SharedSequence(source) } /** Converts anything convertible to `Observable` to `SharedSequence` unit. - parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error. - returns: Driving observable sequence. */ @available(*, deprecated, message: "Please use conversion methods to some SharedSequence specialization.") public func asSharedSequence(sharingStrategy: S.Type = S.self, onErrorRecover: @escaping (_ error: Swift.Error) -> SharedSequence) -> SharedSequence { let source = self .asObservable() .observeOn(S.scheduler) .catchError { error in onErrorRecover(error).asObservable() } return SharedSequence(source) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable+CoreGraphics.swift ================================================ // // KVORepresentable+CoreGraphics.swift // RxCocoa // // Created by Krunoslav Zaher on 11/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !os(Linux) #if !RX_NO_MODULE import RxSwift #endif import CoreGraphics import class Foundation.NSValue #if arch(x86_64) || arch(arm64) let CGRectType = "{CGRect={CGPoint=dd}{CGSize=dd}}" let CGSizeType = "{CGSize=dd}" let CGPointType = "{CGPoint=dd}" #elseif arch(i386) || arch(arm) let CGRectType = "{CGRect={CGPoint=ff}{CGSize=ff}}" let CGSizeType = "{CGSize=ff}" let CGPointType = "{CGPoint=ff}" #endif extension CGRect : KVORepresentable { public typealias KVOType = NSValue /// Constructs self from `NSValue`. public init?(KVOValue: KVOType) { if strcmp(KVOValue.objCType, CGRectType) != 0 { return nil } var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) KVOValue.getValue(&typedValue) self = typedValue } } extension CGPoint : KVORepresentable { public typealias KVOType = NSValue /// Constructs self from `NSValue`. public init?(KVOValue: KVOType) { if strcmp(KVOValue.objCType, CGPointType) != 0 { return nil } var typedValue = CGPoint(x: 0, y: 0) KVOValue.getValue(&typedValue) self = typedValue } } extension CGSize : KVORepresentable { public typealias KVOType = NSValue /// Constructs self from `NSValue`. public init?(KVOValue: KVOType) { if strcmp(KVOValue.objCType, CGSizeType) != 0 { return nil } var typedValue = CGSize(width: 0, height: 0) KVOValue.getValue(&typedValue) self = typedValue } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable+Swift.swift ================================================ // // KVORepresentable+Swift.swift // RxCocoa // // Created by Krunoslav Zaher on 11/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import class Foundation.NSNumber extension Int : KVORepresentable { public typealias KVOType = NSNumber /// Constructs `Self` using KVO value. public init?(KVOValue: KVOType) { self.init(KVOValue.int32Value) } } extension Int32 : KVORepresentable { public typealias KVOType = NSNumber /// Constructs `Self` using KVO value. public init?(KVOValue: KVOType) { self.init(KVOValue.int32Value) } } extension Int64 : KVORepresentable { public typealias KVOType = NSNumber /// Constructs `Self` using KVO value. public init?(KVOValue: KVOType) { self.init(KVOValue.int64Value) } } extension UInt : KVORepresentable { public typealias KVOType = NSNumber /// Constructs `Self` using KVO value. public init?(KVOValue: KVOType) { self.init(KVOValue.uintValue) } } extension UInt32 : KVORepresentable { public typealias KVOType = NSNumber /// Constructs `Self` using KVO value. public init?(KVOValue: KVOType) { self.init(KVOValue.uint32Value) } } extension UInt64 : KVORepresentable { public typealias KVOType = NSNumber /// Constructs `Self` using KVO value. public init?(KVOValue: KVOType) { self.init(KVOValue.uint64Value) } } extension Bool : KVORepresentable { public typealias KVOType = NSNumber /// Constructs `Self` using KVO value. public init?(KVOValue: KVOType) { self.init(KVOValue.boolValue) } } extension RawRepresentable where RawValue: KVORepresentable { /// Constructs `Self` using optional KVO value. init?(KVOValue: RawValue.KVOType?) { guard let KVOValue = KVOValue else { return nil } guard let rawValue = RawValue(KVOValue: KVOValue) else { return nil } self.init(rawValue: rawValue) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable.swift ================================================ // // KVORepresentable.swift // RxCocoa // // Created by Krunoslav Zaher on 11/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Type that is KVO representable (KVO mechanism can be used to observe it). public protocol KVORepresentable { /// Associated KVO type. associatedtype KVOType /// Constructs `Self` using KVO value. init?(KVOValue: KVOType) } extension KVORepresentable { /// Initializes `KVORepresentable` with optional value. init?(KVOValue: KVOType?) { guard let KVOValue = KVOValue else { return nil } self.init(KVOValue: KVOValue) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/Logging.swift ================================================ // // Logging.swift // RxCocoa // // Created by Krunoslav Zaher on 4/3/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.URLRequest /// Simple logging settings for RxCocoa library. public struct Logging { public typealias LogURLRequest = (URLRequest) -> Bool /// Log URL requests to standard output in curl format. public static var URLRequests: LogURLRequest = { _ in #if DEBUG return true #else return false #endif } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift ================================================ // // NSObject+Rx+KVORepresentable.swift // RxCocoa // // Created by Krunoslav Zaher on 11/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !os(Linux) import Foundation.NSObject #if !RX_NO_MODULE import RxSwift #endif /// Key value observing options public struct KeyValueObservingOptions: OptionSet { /// Raw value public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue } /// Whether a sequence element should be sent to the observer immediately, before the subscribe method even returns. public static let initial = KeyValueObservingOptions(rawValue: 1 << 0) /// Whether to send updated values. public static let new = KeyValueObservingOptions(rawValue: 1 << 1) } extension Reactive where Base: NSObject { /** Specialization of generic `observe` method. This is a special overload because to observe values of some type (for example `Int`), first values of KVO type need to be observed (`NSNumber`), and then converted to result type. For more information take a look at `observe` method. */ public func observe(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable { return observe(E.KVOType.self, keyPath, options: options, retainSelf: retainSelf) .map(E.init) } } #if !DISABLE_SWIZZLING && !os(Linux) // KVO extension Reactive where Base: NSObject { /** Specialization of generic `observeWeakly` method. For more information take a look at `observeWeakly` method. */ public func observeWeakly(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable { return observeWeakly(E.KVOType.self, keyPath, options: options) .map(E.init) } } #endif #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift ================================================ // // NSObject+Rx+RawRepresentable.swift // RxCocoa // // Created by Krunoslav Zaher on 11/9/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !os(Linux) import Foundation.NSObject #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: NSObject { /** Specialization of generic `observe` method. This specialization first observes `KVORepresentable` value and then converts it to `RawRepresentable` value. It is useful for observing bridged ObjC enum values. For more information take a look at `observe` method. */ public func observe(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable where E.RawValue: KVORepresentable { return observe(E.RawValue.KVOType.self, keyPath, options: options, retainSelf: retainSelf) .map(E.init) } } #if !DISABLE_SWIZZLING // observeWeakly + RawRepresentable extension Reactive where Base: NSObject { /** Specialization of generic `observeWeakly` method. This specialization first observes `KVORepresentable` value and then converts it to `RawRepresentable` value. It is useful for observing bridged ObjC enum values. For more information take a look at `observeWeakly` method. */ public func observeWeakly(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable where E.RawValue: KVORepresentable { return observeWeakly(E.RawValue.KVOType.self, keyPath, options: options) .map(E.init) } } #endif #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx.swift ================================================ // // NSObject+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !os(Linux) import Foundation.NSObject #if !RX_NO_MODULE import RxSwift #if SWIFT_PACKAGE && !DISABLE_SWIZZLING && !os(Linux) import RxCocoaRuntime #endif #endif #if !DISABLE_SWIZZLING && !os(Linux) fileprivate var deallocatingSubjectTriggerContext: UInt8 = 0 fileprivate var deallocatingSubjectContext: UInt8 = 0 #endif fileprivate var deallocatedSubjectTriggerContext: UInt8 = 0 fileprivate var deallocatedSubjectContext: UInt8 = 0 #if !os(Linux) /** KVO is a tricky mechanism. When observing child in a ownership hierarchy, usually retaining observing target is wanted behavior. When observing parent in a ownership hierarchy, usually retaining target isn't wanter behavior. KVO with weak references is especially tricky. For it to work, some kind of swizzling is required. That can be done by * replacing object class dynamically (like KVO does) * by swizzling `dealloc` method on all instances for a class. * some third method ... Both approaches can fail in certain scenarios: * problems arise when swizzlers return original object class (like KVO does when nobody is observing) * Problems can arise because replacing dealloc method isn't atomic operation (get implementation, set implementation). Second approach is chosen. It can fail in case there are multiple libraries dynamically trying to replace dealloc method. In case that isn't the case, it should be ok. */ extension Reactive where Base: NSObject { /** Observes values on `keyPath` starting from `self` with `options` and retains `self` if `retainSelf` is set. `observe` is just a simple and performant wrapper around KVO mechanism. * it can be used to observe paths starting from `self` or from ancestors in ownership graph (`retainSelf = false`) * it can be used to observe paths starting from descendants in ownership graph (`retainSelf = true`) * the paths have to consist only of `strong` properties, otherwise you are risking crashing the system by not unregistering KVO observer before dealloc. If support for weak properties is needed or observing arbitrary or unknown relationships in the ownership tree, `observeWeakly` is the preferred option. - parameter keyPath: Key path of property names to observe. - parameter options: KVO mechanism notification options. - parameter retainSelf: Retains self during observation if set `true`. - returns: Observable sequence of objects on `keyPath`. */ public func observe(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable { return KVOObservable(object: base, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable() } } #endif #if !DISABLE_SWIZZLING && !os(Linux) // KVO extension Reactive where Base: NSObject { /** Observes values on `keyPath` starting from `self` with `options` and doesn't retain `self`. It can be used in all cases where `observe` can be used and additionally * because it won't retain observed target, it can be used to observe arbitrary object graph whose ownership relation is unknown * it can be used to observe `weak` properties **Since it needs to intercept object deallocation process it needs to perform swizzling of `dealloc` method on observed object.** - parameter keyPath: Key path of property names to observe. - parameter options: KVO mechanism notification options. - returns: Observable sequence of objects on `keyPath`. */ public func observeWeakly(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable { return observeWeaklyKeyPathFor(base, keyPath: keyPath, options: options) .map { n in return n as? E } } } #endif // Dealloc extension Reactive where Base: AnyObject { /** Observable sequence of object deallocated events. After object is deallocated one `()` element will be produced and sequence will immediately complete. - returns: Observable sequence of object deallocated events. */ public var deallocated: Observable { return synchronized { if let deallocObservable = objc_getAssociatedObject(base, &deallocatedSubjectContext) as? DeallocObservable { return deallocObservable._subject } let deallocObservable = DeallocObservable() objc_setAssociatedObject(base, &deallocatedSubjectContext, deallocObservable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return deallocObservable._subject } } #if !DISABLE_SWIZZLING && !os(Linux) /** Observable sequence of message arguments that completes when object is deallocated. Each element is produced before message is invoked on target object. `methodInvoked` exists in case observing of invoked messages is needed. In case an error occurs sequence will fail with `RxCocoaObjCRuntimeError`. In case some argument is `nil`, instance of `NSNull()` will be sent. - returns: Observable sequence of arguments passed to `selector` method. */ public func sentMessage(_ selector: Selector) -> Observable<[Any]> { return synchronized { // in case of dealloc selector replay subject behavior needs to be used if selector == deallocSelector { return deallocating.map { _ in [] } } do { let proxy: MessageSentProxy = try registerMessageInterceptor(selector) return proxy.messageSent.asObservable() } catch let e { return Observable.error(e) } } } /** Observable sequence of message arguments that completes when object is deallocated. Each element is produced after message is invoked on target object. `sentMessage` exists in case interception of sent messages before they were invoked is needed. In case an error occurs sequence will fail with `RxCocoaObjCRuntimeError`. In case some argument is `nil`, instance of `NSNull()` will be sent. - returns: Observable sequence of arguments passed to `selector` method. */ public func methodInvoked(_ selector: Selector) -> Observable<[Any]> { return synchronized { // in case of dealloc selector replay subject behavior needs to be used if selector == deallocSelector { return deallocated.map { _ in [] } } do { let proxy: MessageSentProxy = try registerMessageInterceptor(selector) return proxy.methodInvoked.asObservable() } catch let e { return Observable.error(e) } } } /** Observable sequence of object deallocating events. When `dealloc` message is sent to `self` one `()` element will be produced and after object is deallocated sequence will immediately complete. In case an error occurs sequence will fail with `RxCocoaObjCRuntimeError`. - returns: Observable sequence of object deallocating events. */ public var deallocating: Observable<()> { return synchronized { do { let proxy: DeallocatingProxy = try registerMessageInterceptor(deallocSelector) return proxy.messageSent.asObservable() } catch let e { return Observable.error(e) } } } fileprivate func registerMessageInterceptor(_ selector: Selector) throws -> T { let rxSelector = RX_selector(selector) let selectorReference = RX_reference_from_selector(rxSelector) let subject: T if let existingSubject = objc_getAssociatedObject(base, selectorReference) as? T { subject = existingSubject } else { subject = T() objc_setAssociatedObject( base, selectorReference, subject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } if subject.isActive { return subject } var error: NSError? let targetImplementation = RX_ensure_observing(base, selector, &error) if targetImplementation == nil { throw error?.rxCocoaErrorForTarget(base) ?? RxCocoaError.unknown } subject.targetImplementation = targetImplementation! return subject } #endif } // MARK: Message interceptors #if !DISABLE_SWIZZLING && !os(Linux) fileprivate protocol MessageInterceptorSubject: class { init() var isActive: Bool { get } var targetImplementation: IMP { get set } } fileprivate final class DeallocatingProxy : MessageInterceptorSubject , RXDeallocatingObserver { typealias E = () let messageSent = ReplaySubject<()>.create(bufferSize: 1) @objc var targetImplementation: IMP = RX_default_target_implementation() var isActive: Bool { return targetImplementation != RX_default_target_implementation() } init() { } @objc func deallocating() -> Void { messageSent.on(.next(())) } deinit { messageSent.on(.completed) } } fileprivate final class MessageSentProxy : MessageInterceptorSubject , RXMessageSentObserver { typealias E = [AnyObject] let messageSent = PublishSubject<[Any]>() let methodInvoked = PublishSubject<[Any]>() @objc var targetImplementation: IMP = RX_default_target_implementation() var isActive: Bool { return targetImplementation != RX_default_target_implementation() } init() { } @objc func messageSent(withArguments arguments: [Any]) -> Void { messageSent.on(.next(arguments)) } @objc func methodInvoked(withArguments arguments: [Any]) -> Void { methodInvoked.on(.next(arguments)) } deinit { messageSent.on(.completed) methodInvoked.on(.completed) } } #endif fileprivate final class DeallocObservable { let _subject = ReplaySubject.create(bufferSize:1) init() { } deinit { _subject.on(.next(())) _subject.on(.completed) } } // MARK: KVO #if !os(Linux) fileprivate protocol KVOObservableProtocol { var target: AnyObject { get } var keyPath: String { get } var retainTarget: Bool { get } var options: KeyValueObservingOptions { get } } fileprivate final class KVOObserver : _RXKVOObserver , Disposable { typealias Callback = (Any?) -> Void var retainSelf: KVOObserver? = nil init(parent: KVOObservableProtocol, callback: @escaping Callback) { #if TRACE_RESOURCES _ = Resources.incrementTotal() #endif super.init(target: parent.target, retainTarget: parent.retainTarget, keyPath: parent.keyPath, options: parent.options.nsOptions, callback: callback) self.retainSelf = self } override func dispose() { super.dispose() self.retainSelf = nil } deinit { #if TRACE_RESOURCES _ = Resources.decrementTotal() #endif } } fileprivate final class KVOObservable : ObservableType , KVOObservableProtocol { typealias E = Element? unowned var target: AnyObject var strongTarget: AnyObject? var keyPath: String var options: KeyValueObservingOptions var retainTarget: Bool init(object: AnyObject, keyPath: String, options: KeyValueObservingOptions, retainTarget: Bool) { self.target = object self.keyPath = keyPath self.options = options self.retainTarget = retainTarget if retainTarget { self.strongTarget = object } } func subscribe(_ observer: O) -> Disposable where O.E == Element? { let observer = KVOObserver(parent: self) { (value) in if value as? NSNull != nil { observer.on(.next(nil)) return } observer.on(.next(value as? Element)) } return Disposables.create(with: observer.dispose) } } #endif #if !DISABLE_SWIZZLING && !os(Linux) fileprivate func observeWeaklyKeyPathFor(_ target: NSObject, keyPath: String, options: KeyValueObservingOptions) -> Observable { let components = keyPath.components(separatedBy: ".").filter { $0 != "self" } let observable = observeWeaklyKeyPathFor(target, keyPathSections: components, options: options) .finishWithNilWhenDealloc(target) if !options.intersection(.initial).isEmpty { return observable } else { return observable .skip(1) } } // This should work correctly // Identifiers can't contain `,`, so the only place where `,` can appear // is as a delimiter. // This means there is `W` as element in an array of property attributes. fileprivate func isWeakProperty(_ properyRuntimeInfo: String) -> Bool { return properyRuntimeInfo.range(of: ",W,") != nil } fileprivate extension ObservableType where E == AnyObject? { func finishWithNilWhenDealloc(_ target: NSObject) -> Observable { let deallocating = target.rx.deallocating return deallocating .map { _ in return Observable.just(nil) } .startWith(self.asObservable()) .switchLatest() } } fileprivate extension KeyValueObservingOptions { fileprivate var nsOptions: NSKeyValueObservingOptions { var result: UInt = 0 if self.contains(.new) { result |= NSKeyValueObservingOptions.new.rawValue } if self.contains(.initial) { result |= NSKeyValueObservingOptions.initial.rawValue } return NSKeyValueObservingOptions(rawValue: result) } } fileprivate func observeWeaklyKeyPathFor( _ target: NSObject, keyPathSections: [String], options: KeyValueObservingOptions ) -> Observable { weak var weakTarget: AnyObject? = target let propertyName = keyPathSections[0] let remainingPaths = Array(keyPathSections[1.. // KVO recursion for value changes return propertyObservable .flatMapLatest { (nextTarget: AnyObject?) -> Observable in if nextTarget == nil { return Observable.just(nil) } let nextObject = nextTarget! as? NSObject let strongTarget: AnyObject? = weakTarget if nextObject == nil { return Observable.error(RxCocoaError.invalidObjectOnKeyPath(object: nextTarget!, sourceObject: strongTarget ?? NSNull(), propertyName: propertyName)) } // if target is alive, then send change // if it's deallocated, don't send anything if strongTarget == nil { return Observable.empty() } let nextElementsObservable = keyPathSections.count == 1 ? Observable.just(nextTarget) : observeWeaklyKeyPathFor(nextObject!, keyPathSections: remainingPaths, options: options) if isWeak { return nextElementsObservable .finishWithNilWhenDealloc(nextObject!) } else { return nextElementsObservable } } } #endif // MARK Constants fileprivate let deallocSelector = NSSelectorFromString("dealloc") // MARK: AnyObject + Reactive extension Reactive where Base: AnyObject { func synchronized( _ action: () -> T) -> T { objc_sync_enter(self.base) let result = action() objc_sync_exit(self.base) return result } } extension Reactive where Base: AnyObject { /** Helper to make sure that `Observable` returned from `createCachedObservable` is only created once. This is important because there is only one `target` and `action` properties on `NSControl` or `UIBarButtonItem`. */ func lazyInstanceObservable(_ key: UnsafeRawPointer, createCachedObservable: () -> T) -> T { if let value = objc_getAssociatedObject(base, key) { return value as! T } let observable = createCachedObservable() objc_setAssociatedObject(base, key, observable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return observable } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/NotificationCenter+Rx.swift ================================================ // // NotificationCenter+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 5/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import class Foundation.NotificationCenter import struct Foundation.Notification #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: NotificationCenter { /** Transforms notifications posted to notification center to observable sequence of notifications. - parameter name: Optional name used to filter notifications. - parameter object: Optional object used to filter notifications. - returns: Observable sequence of posted notifications. */ public func notification(_ name: Notification.Name?, object: AnyObject? = nil) -> Observable { return Observable.create { [weak object] observer in let nsObserver = self.base.addObserver(forName: name, object: object, queue: nil) { notification in observer.on(.next(notification)) } return Disposables.create { self.base.removeObserver(nsObserver) } } } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Foundation/URLSession+Rx.swift ================================================ // // URLSession+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 3/23/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.URL import struct Foundation.URLRequest import struct Foundation.Data import struct Foundation.Date import struct Foundation.TimeInterval import class Foundation.HTTPURLResponse import class Foundation.URLSession import class Foundation.URLResponse import class Foundation.JSONSerialization import class Foundation.NSError import var Foundation.NSURLErrorCancelled import var Foundation.NSURLErrorDomain #if os(Linux) // don't know why import Foundation #endif #if !RX_NO_MODULE import RxSwift #endif /// RxCocoa URL errors. public enum RxCocoaURLError : Swift.Error { /// Unknown error occurred. case unknown /// Response is not NSHTTPURLResponse case nonHTTPResponse(response: URLResponse) /// Response is not successful. (not in `200 ..< 300` range) case httpRequestFailed(response: HTTPURLResponse, data: Data?) /// Deserialization error. case deserializationError(error: Swift.Error) } extension RxCocoaURLError : CustomDebugStringConvertible { /// A textual representation of `self`, suitable for debugging. public var debugDescription: String { switch self { case .unknown: return "Unknown error has occurred." case let .nonHTTPResponse(response): return "Response is not NSHTTPURLResponse `\(response)`." case let .httpRequestFailed(response, _): return "HTTP request failed with `\(response.statusCode)`." case let .deserializationError(error): return "Error during deserialization of the response: \(error)" } } } fileprivate func escapeTerminalString(_ value: String) -> String { return value.replacingOccurrences(of: "\"", with: "\\\"", options:[], range: nil) } fileprivate func convertURLRequestToCurlCommand(_ request: URLRequest) -> String { let method = request.httpMethod ?? "GET" var returnValue = "curl -X \(method) " if let httpBody = request.httpBody, request.httpMethod == "POST" { let maybeBody = String(data: httpBody, encoding: String.Encoding.utf8) if let body = maybeBody { returnValue += "-d \"\(escapeTerminalString(body))\" " } } for (key, value) in request.allHTTPHeaderFields ?? [:] { let escapedKey = escapeTerminalString(key as String) let escapedValue = escapeTerminalString(value as String) returnValue += "\n -H \"\(escapedKey): \(escapedValue)\" " } let URLString = request.url?.absoluteString ?? "" returnValue += "\n\"\(escapeTerminalString(URLString))\"" returnValue += " -i -v" return returnValue } fileprivate func convertResponseToString(_ response: URLResponse?, _ error: NSError?, _ interval: TimeInterval) -> String { let ms = Int(interval * 1000) if let response = response as? HTTPURLResponse { if 200 ..< 300 ~= response.statusCode { return "Success (\(ms)ms): Status \(response.statusCode)" } else { return "Failure (\(ms)ms): Status \(response.statusCode)" } } if let error = error { if error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled { return "Canceled (\(ms)ms)" } return "Failure (\(ms)ms): NSError > \(error)" } return "" } extension Reactive where Base: URLSession { /** Observable sequence of responses for URL request. Performing of request starts after observer is subscribed and not after invoking this method. **URL requests will be performed per subscribed observer.** Any error during fetching of the response will cause observed sequence to terminate with error. - parameter request: URL request. - returns: Observable sequence of URL responses. */ public func response(request: URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)> { return Observable.create { observer in // smart compiler should be able to optimize this out let d: Date? if Logging.URLRequests(request) { d = Date() } else { d = nil } let task = self.base.dataTask(with: request) { (data, response, error) in if Logging.URLRequests(request) { let interval = Date().timeIntervalSince(d ?? Date()) print(convertURLRequestToCurlCommand(request)) #if os(Linux) print(convertResponseToString(response, error.flatMap { $0 as? NSError }, interval)) #else print(convertResponseToString(response, error.map { $0 as NSError }, interval)) #endif } guard let response = response, let data = data else { observer.on(.error(error ?? RxCocoaURLError.unknown)) return } guard let httpResponse = response as? HTTPURLResponse else { observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response))) return } observer.on(.next((httpResponse, data))) observer.on(.completed) } task.resume() return Disposables.create(with: task.cancel) } } /** Observable sequence of response data for URL request. Performing of request starts after observer is subscribed and not after invoking this method. **URL requests will be performed per subscribed observer.** Any error during fetching of the response will cause observed sequence to terminate with error. If response is not HTTP response with status code in the range of `200 ..< 300`, sequence will terminate with `(RxCocoaErrorDomain, RxCocoaError.NetworkError)`. - parameter request: URL request. - returns: Observable sequence of response data. */ public func data(request: URLRequest) -> Observable { return response(request: request).map { pair -> Data in if 200 ..< 300 ~= pair.0.statusCode { return pair.1 } else { throw RxCocoaURLError.httpRequestFailed(response: pair.0, data: pair.1) } } } /** Observable sequence of response JSON for URL request. Performing of request starts after observer is subscribed and not after invoking this method. **URL requests will be performed per subscribed observer.** Any error during fetching of the response will cause observed sequence to terminate with error. If response is not HTTP response with status code in the range of `200 ..< 300`, sequence will terminate with `(RxCocoaErrorDomain, RxCocoaError.NetworkError)`. If there is an error during JSON deserialization observable sequence will fail with that error. - parameter request: URL request. - returns: Observable sequence of response JSON. */ public func json(request: URLRequest, options: JSONSerialization.ReadingOptions = []) -> Observable { return data(request: request).map { (data) -> Any in do { return try JSONSerialization.jsonObject(with: data, options: options) } catch let error { throw RxCocoaURLError.deserializationError(error: error) } } } /** Observable sequence of response JSON for GET request with `URL`. Performing of request starts after observer is subscribed and not after invoking this method. **URL requests will be performed per subscribed observer.** Any error during fetching of the response will cause observed sequence to terminate with error. If response is not HTTP response with status code in the range of `200 ..< 300`, sequence will terminate with `(RxCocoaErrorDomain, RxCocoaError.NetworkError)`. If there is an error during JSON deserialization observable sequence will fail with that error. - parameter url: URL of `NSURLRequest` request. - returns: Observable sequence of response JSON. */ public func json(url: Foundation.URL) -> Observable { return json(request: URLRequest(url: url)) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/_RX.m ================================================ // // _RX.m // RxCocoa // // Created by Krunoslav Zaher on 7/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import "include/_RX.h" ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/_RXDelegateProxy.m ================================================ // // _RXDelegateProxy.m // RxCocoa // // Created by Krunoslav Zaher on 7/4/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import "include/_RXDelegateProxy.h" #import "include/_RX.h" #import "include/_RXObjCRuntime.h" @interface _RXDelegateProxy () { id __weak __forwardToDelegate; } @property (nonatomic, strong) id strongForwardDelegate; @end static NSMutableDictionary *voidSelectorsPerClass = nil; @implementation _RXDelegateProxy +(NSSet*)collectVoidSelectorsForProtocol:(Protocol *)protocol { NSMutableSet *selectors = [NSMutableSet set]; unsigned int protocolMethodCount = 0; struct objc_method_description *pMethods = protocol_copyMethodDescriptionList(protocol, NO, YES, &protocolMethodCount); for (unsigned int i = 0; i < protocolMethodCount; ++i) { struct objc_method_description method = pMethods[i]; if (RX_is_method_with_description_void(method)) { [selectors addObject:SEL_VALUE(method.name)]; } } free(pMethods); unsigned int numberOfBaseProtocols = 0; Protocol * __unsafe_unretained * pSubprotocols = protocol_copyProtocolList(protocol, &numberOfBaseProtocols); for (unsigned int i = 0; i < numberOfBaseProtocols; ++i) { [selectors unionSet:[self collectVoidSelectorsForProtocol:pSubprotocols[i]]]; } free(pSubprotocols); return selectors; } +(void)initialize { @synchronized (_RXDelegateProxy.class) { if (voidSelectorsPerClass == nil) { voidSelectorsPerClass = [[NSMutableDictionary alloc] init]; } NSMutableSet *voidSelectors = [NSMutableSet set]; #define CLASS_HIERARCHY_MAX_DEPTH 100 NSInteger classHierarchyDepth = 0; Class targetClass = NULL; for (classHierarchyDepth = 0, targetClass = self; classHierarchyDepth < CLASS_HIERARCHY_MAX_DEPTH && targetClass != nil; ++classHierarchyDepth, targetClass = class_getSuperclass(targetClass) ) { unsigned int count; Protocol *__unsafe_unretained *pProtocols = class_copyProtocolList(targetClass, &count); for (unsigned int i = 0; i < count; i++) { NSSet *selectorsForProtocol = [self collectVoidSelectorsForProtocol:pProtocols[i]]; [voidSelectors unionSet:selectorsForProtocol]; } free(pProtocols); } if (classHierarchyDepth == CLASS_HIERARCHY_MAX_DEPTH) { NSLog(@"Detected weird class hierarchy with depth over %d. Starting with this class -> %@", CLASS_HIERARCHY_MAX_DEPTH, self); #if DEBUG abort(); #endif } voidSelectorsPerClass[CLASS_VALUE(self)] = voidSelectors; } } -(id)_forwardToDelegate { return __forwardToDelegate; } -(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate { __forwardToDelegate = forwardToDelegate; if (retainDelegate) { self.strongForwardDelegate = forwardToDelegate; } else { self.strongForwardDelegate = nil; } } -(BOOL)hasWiredImplementationForSelector:(SEL)selector { return [super respondsToSelector:selector]; } -(BOOL)voidDelegateMethodsContain:(SEL)selector { @synchronized(_RXDelegateProxy.class) { NSSet *voidSelectors = voidSelectorsPerClass[CLASS_VALUE(self.class)]; NSAssert(voidSelectors != nil, @"Set of allowed methods not initialized"); return [voidSelectors containsObject:SEL_VALUE(selector)]; } } -(void)forwardInvocation:(NSInvocation *)anInvocation { BOOL isVoid = RX_is_method_signature_void(anInvocation.methodSignature); NSArray *arguments = nil; if (isVoid) { arguments = RX_extract_arguments(anInvocation); [self _sentMessage:anInvocation.selector withArguments:arguments]; } if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:self._forwardToDelegate]; } if (isVoid) { [self _methodInvoked:anInvocation.selector withArguments:arguments]; } } // abstract method -(void)_sentMessage:(SEL)selector withArguments:(NSArray *)arguments { } // abstract method -(void)_methodInvoked:(SEL)selector withArguments:(NSArray *)arguments { } -(void)dealloc { } @end ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/_RXKVOObserver.m ================================================ // // _RXKVOObserver.m // RxCocoa // // Created by Krunoslav Zaher on 7/11/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import "include/_RXKVOObserver.h" @interface _RXKVOObserver () @property (nonatomic, unsafe_unretained) id target; @property (nonatomic, strong ) id retainedTarget; @property (nonatomic, copy ) NSString *keyPath; @property (nonatomic, copy ) void (^callback)(id); @end @implementation _RXKVOObserver -(instancetype)initWithTarget:(id)target retainTarget:(BOOL)retainTarget keyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options callback:(void (^)(id))callback { self = [super init]; if (!self) return nil; self.target = target; if (retainTarget) { self.retainedTarget = target; } self.keyPath = keyPath; self.callback = callback; [self.target addObserver:self forKeyPath:self.keyPath options:options context:nil]; return self; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { @synchronized(self) { self.callback(change[NSKeyValueChangeNewKey]); } } -(void)dispose { [self.target removeObserver:self forKeyPath:self.keyPath context:nil]; self.target = nil; self.retainedTarget = nil; } @end ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/_RXObjCRuntime.m ================================================ // // _RXObjCRuntime.m // RxCocoa // // Created by Krunoslav Zaher on 7/11/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import #import #import #import #import #import "include/_RX.h" #import "include/_RXObjCRuntime.h" #if !DISABLE_SWIZZLING #define NSErrorParam NSError *__autoreleasing __nullable * __nullable // self + cmd #define HIDDEN_ARGUMENT_COUNT 2 @class RXObjCRuntime; BOOL RXAbortOnThreadingHazard = NO; typedef NSInvocation *NSInvocationRef; typedef NSMethodSignature *NSMethodSignatureRef; typedef unsigned char rx_uchar; typedef unsigned short rx_ushort; typedef unsigned int rx_uint; typedef unsigned long rx_ulong; typedef id (^rx_block)(id); typedef BOOL (^RXInterceptWithOptimizedObserver)(RXObjCRuntime * __nonnull self, Class __nonnull class, SEL __nonnull selector, NSErrorParam error); static CFTypeID defaultTypeID; static SEL deallocSelector; static int RxSwizzlingTargetClassKey = 0; #if TRACE_RESOURCES static int32_t numberOInterceptedMethods = 0; static int32_t numberOfForwardedMethods = 0; #endif #define THREADING_HAZARD(class) \ NSLog(@"There was a problem swizzling on `%@`.\nYou have probably two libraries performing swizzling in runtime.\nWe didn't want to crash your program, but this is not good ...\nYou an solve this problem by either not using swizzling in this library, removing one of those other libraries, or making sure that swizzling parts are synchronized (only perform them on main thread).\nAnd yes, this message will self destruct when you clear the console, and since it's non deterministic, the problem could still exist and it will be hard for you to reproduce it.", NSStringFromClass(class)); ABORT_IN_DEBUG if (RXAbortOnThreadingHazard) { abort(); } #define ALWAYS(condition, message) if (!(condition)) { [NSException raise:@"RX Invalid Operator" format:@"%@", message]; } #define ALWAYS_WITH_INFO(condition, message) NSAssert((condition), @"%@ [%@] > %@", NSStringFromClass(class), NSStringFromSelector(selector), (message)) #define C_ALWAYS(condition, message) NSCAssert((condition), @"%@ [%@] > %@", NSStringFromClass(class), NSStringFromSelector(selector), (message)) #define RX_PREFIX @"_RX_namespace_" #define RX_ARG_id(value) ((value) ?: [NSNull null]) #define RX_ARG_char(value) [NSNumber numberWithChar:value] #define RX_ARG_short(value) [NSNumber numberWithShort:value] #define RX_ARG_int(value) [NSNumber numberWithInt:value] #define RX_ARG_long(value) [NSNumber numberWithLong:value] #define RX_ARG_BOOL(value) [NSNumber numberWithBool:value] #define RX_ARG_SEL(value) [NSNumber valueWithPointer:value] #define RX_ARG_rx_uchar(value) [NSNumber numberWithUnsignedInt:value] #define RX_ARG_rx_ushort(value) [NSNumber numberWithUnsignedInt:value] #define RX_ARG_rx_uint(value) [NSNumber numberWithUnsignedInt:value] #define RX_ARG_rx_ulong(value) [NSNumber numberWithUnsignedLong:value] #define RX_ARG_rx_block(value) ((id)(value) ?: [NSNull null]) #define RX_ARG_float(value) [NSNumber numberWithFloat:value] #define RX_ARG_double(value) [NSNumber numberWithDouble:value] typedef struct supported_type { const char *encoding; } supported_type_t; static supported_type_t supported_types[] = { { .encoding = @encode(void)}, { .encoding = @encode(id)}, { .encoding = @encode(Class)}, { .encoding = @encode(void (^)(void))}, { .encoding = @encode(char)}, { .encoding = @encode(short)}, { .encoding = @encode(int)}, { .encoding = @encode(long)}, { .encoding = @encode(long long)}, { .encoding = @encode(unsigned char)}, { .encoding = @encode(unsigned short)}, { .encoding = @encode(unsigned int)}, { .encoding = @encode(unsigned long)}, { .encoding = @encode(unsigned long long)}, { .encoding = @encode(float)}, { .encoding = @encode(double)}, { .encoding = @encode(BOOL)}, { .encoding = @encode(const char*)}, }; NSString * __nonnull const RXObjCRuntimeErrorDomain = @"RXObjCRuntimeErrorDomain"; NSString * __nonnull const RXObjCRuntimeErrorIsKVOKey = @"RXObjCRuntimeErrorIsKVOKey"; BOOL RX_return_type_is_supported(const char *type) { if (type == nil) { return NO; } for (int i = 0; i < sizeof(supported_types) / sizeof(supported_type_t); ++i) { if (supported_types[i].encoding[0] != type[0]) { continue; } if (strcmp(supported_types[i].encoding, type) == 0) { return YES; } } return NO; } static BOOL RX_method_has_supported_return_type(Method method) { const char *rawEncoding = method_getTypeEncoding(method); ALWAYS(rawEncoding != nil, @"Example encoding method is nil."); NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:rawEncoding]; ALWAYS(methodSignature != nil, @"Method signature method is nil."); return RX_return_type_is_supported(methodSignature.methodReturnType); } SEL __nonnull RX_selector(SEL __nonnull selector) { NSString *selectorString = NSStringFromSelector(selector); return NSSelectorFromString([RX_PREFIX stringByAppendingString:selectorString]); } BOOL RX_is_method_signature_void(NSMethodSignature * __nonnull methodSignature) { const char *methodReturnType = methodSignature.methodReturnType; return strcmp(methodReturnType, @encode(void)) == 0; } BOOL RX_is_method_with_description_void(struct objc_method_description method) { return strncmp(method.types, @encode(void), 1) == 0; } id __nonnull RX_extract_argument_at_index(NSInvocation * __nonnull invocation, NSUInteger index) { const char *argumentType = [invocation.methodSignature getArgumentTypeAtIndex:index]; #define RETURN_VALUE(type) \ else if (strcmp(argumentType, @encode(type)) == 0) {\ type val = 0; \ [invocation getArgument:&val atIndex:index]; \ return @(val); \ } // Skip const type qualifier. if (argumentType[0] == 'r') { argumentType++; } if (strcmp(argumentType, @encode(id)) == 0 || strcmp(argumentType, @encode(Class)) == 0 || strcmp(argumentType, @encode(void (^)(void))) == 0 ) { __unsafe_unretained id argument = nil; [invocation getArgument:&argument atIndex:index]; return argument; } RETURN_VALUE(char) RETURN_VALUE(short) RETURN_VALUE(int) RETURN_VALUE(long) RETURN_VALUE(long long) RETURN_VALUE(unsigned char) RETURN_VALUE(unsigned short) RETURN_VALUE(unsigned int) RETURN_VALUE(unsigned long) RETURN_VALUE(unsigned long long) RETURN_VALUE(float) RETURN_VALUE(double) RETURN_VALUE(BOOL) RETURN_VALUE(const char *) else { NSUInteger size = 0; NSGetSizeAndAlignment(argumentType, &size, NULL); NSCParameterAssert(size > 0); uint8_t data[size]; [invocation getArgument:&data atIndex:index]; return [NSValue valueWithBytes:&data objCType:argumentType]; } } NSArray *RX_extract_arguments(NSInvocation *invocation) { NSUInteger numberOfArguments = invocation.methodSignature.numberOfArguments; NSUInteger numberOfVisibleArguments = numberOfArguments - HIDDEN_ARGUMENT_COUNT; NSCParameterAssert(numberOfVisibleArguments >= 0); NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:numberOfVisibleArguments]; for (NSUInteger index = HIDDEN_ARGUMENT_COUNT; index < numberOfArguments; ++index) { [arguments addObject:RX_extract_argument_at_index(invocation, index) ?: [NSNull null]]; } return arguments; } void * __nonnull RX_reference_from_selector(SEL __nonnull selector) { return selector; } static BOOL RX_forward_invocation(id __nonnull __unsafe_unretained self, NSInvocation *invocation) { SEL originalSelector = RX_selector(invocation.selector); id messageSentObserver = objc_getAssociatedObject(self, originalSelector); if (messageSentObserver != nil) { NSArray *arguments = RX_extract_arguments(invocation); [messageSentObserver messageSentWithArguments:arguments]; } if ([self respondsToSelector:originalSelector]) { invocation.selector = originalSelector; [invocation invokeWithTarget:self]; if (messageSentObserver != nil) { NSArray *arguments = RX_extract_arguments(invocation); [messageSentObserver methodInvokedWithArguments:arguments]; } return YES; } return NO; } static BOOL RX_responds_to_selector(id __nonnull __unsafe_unretained self, SEL selector) { Class class = object_getClass(self); if (class == nil) { return NO; } Method m = class_getInstanceMethod(class, selector); return m != nil; } static NSMethodSignatureRef RX_method_signature(id __nonnull __unsafe_unretained self, SEL selector) { Class class = object_getClass(self); if (class == nil) { return nil; } Method method = class_getInstanceMethod(class, selector); if (method == nil) { return nil; } const char *encoding = method_getTypeEncoding(method); if (encoding == nil) { return nil; } return [NSMethodSignature signatureWithObjCTypes:encoding]; } static NSString * __nonnull RX_method_encoding(Method __nonnull method) { const char *typeEncoding = method_getTypeEncoding(method); ALWAYS(typeEncoding != nil, @"Method encoding is nil."); NSString *encoding = [NSString stringWithCString:typeEncoding encoding:NSASCIIStringEncoding]; ALWAYS(encoding != nil, @"Can't convert encoding to NSString."); return encoding; } @interface RXObjCRuntime: NSObject @property (nonatomic, assign) pthread_mutex_t lock; @property (nonatomic, strong) NSMutableSet *classesThatSupportObservingByForwarding; @property (nonatomic, strong) NSMutableDictionary *> *forwardedSelectorsByClass; @property (nonatomic, strong) NSMutableDictionary *dynamicSubclassByRealClass; @property (nonatomic, strong) NSMutableDictionary*> *interceptorIMPbySelectorsByClass; +(RXObjCRuntime*)instance; -(void)performLocked:(void (^)(RXObjCRuntime* __nonnull))action; -(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull)selector error:(NSErrorParam)error; -(BOOL)ensureSwizzledSelector:(SEL __nonnull)selector ofClass:(Class __nonnull)class newImplementationGenerator:(IMP(^)(void))newImplementationGenerator replacementImplementationGenerator:(IMP (^)(IMP originalImplementation))replacementImplementationGenerator error:(NSErrorParam)error; +(void)registerOptimizedObserver:(RXInterceptWithOptimizedObserver)registration encodedAs:(SEL)selector; @end /** All API methods perform work on locked instance of `RXObjCRuntime`. In that way it's easy to prove that every action is properly locked. */ IMP __nullable RX_ensure_observing(id __nonnull target, SEL __nonnull selector, NSErrorParam error) { __block IMP targetImplementation = nil; // Target is the second object that needs to be synchronized to TRY to make sure other swizzling framework // won't do something in parallel. // Even though this is too fine grained locking and more coarse grained locks should exist, this is just in case // someone calls this method directly without any external lock. @synchronized(target) { // The only other resource that all other swizzling libraries have in common without introducing external // dependencies is class object. // // It is polite to try to synchronize it in hope other unknown entities will also attempt to do so. // It's like trying to figure out how to communicate with aliens without actually communicating, // save for the fact that aliens are people, programmers, authors of swizzling libraries. @synchronized([target class]) { [[RXObjCRuntime instance] performLocked:^(RXObjCRuntime * __nonnull self) { targetImplementation = [self ensurePrepared:target forObserving:selector error:error]; }]; } } return targetImplementation; } IMP __nonnull RX_default_target_implementation(void) { return _objc_msgForward; } // bodies #define FORWARD_BODY(invocation) if (RX_forward_invocation(self, NAME_CAT(_, 0, invocation))) { return; } #define RESPONDS_TO_SELECTOR_BODY(selector) if (RX_responds_to_selector(self, NAME_CAT(_, 0, selector))) return YES; #define CLASS_BODY(...) return actAsClass; #define METHOD_SIGNATURE_FOR_SELECTOR_BODY(selector) \ NSMethodSignatureRef methodSignature = RX_method_signature(self, NAME_CAT(_, 0, selector)); \ if (methodSignature != nil) { \ return methodSignature; \ } #define DEALLOCATING_BODY(...) \ id observer = objc_getAssociatedObject(self, rxSelector); \ if (observer != nil && observer.targetImplementation == thisIMP) { \ [observer deallocating]; \ } #define OBSERVE_BODY(...) \ id observer = objc_getAssociatedObject(self, rxSelector); \ \ if (observer != nil && observer.targetImplementation == thisIMP) { \ [observer messageSentWithArguments:@[COMMA_DELIMITED_ARGUMENTS(__VA_ARGS__)]]; \ } \ #define OBSERVE_INVOKED_BODY(...) \ if (observer != nil && observer.targetImplementation == thisIMP) { \ [observer methodInvokedWithArguments:@[COMMA_DELIMITED_ARGUMENTS(__VA_ARGS__)]]; \ } \ #define BUILD_ARG_WRAPPER(type) RX_ARG_ ## type //RX_ARG_ ## type #define CAT(_1, _2, head, tail) RX_CAT2(head, tail) #define SEPARATE_BY_COMMA(_1, _2, head, tail) head, tail #define SEPARATE_BY_SPACE(_1, _2, head, tail) head tail #define SEPARATE_BY_UNDERSCORE(head, tail) RX_CAT2(RX_CAT2(head, _), tail) #define UNDERSCORE_TYPE_CAT(_1, index, type) RX_CAT2(_, type) // generates -> _type #define NAME_CAT(_1, index, type) SEPARATE_BY_UNDERSCORE(type, index) // generates -> type_0 #define TYPE_AND_NAME_CAT(_1, index, type) type SEPARATE_BY_UNDERSCORE(type, index) // generates -> type type_0 #define NOT_NULL_ARGUMENT_CAT(_1, index, type) BUILD_ARG_WRAPPER(type)(NAME_CAT(_1, index, type)) // generates -> ((id)(type_0) ?: [NSNull null]) #define EXAMPLE_PARAMETER(_1, index, type) RX_CAT2(_, type):(type)SEPARATE_BY_UNDERSCORE(type, index) // generates -> _type:(type)type_0 #define SELECTOR_PART(_1, index, type) RX_CAT2(_, type:) // generates -> _type: #define COMMA_DELIMITED_ARGUMENTS(...) RX_FOREACH(_, SEPARATE_BY_COMMA, NOT_NULL_ARGUMENT_CAT, ## __VA_ARGS__) #define ARGUMENTS(...) RX_FOREACH_COMMA(_, NAME_CAT, ## __VA_ARGS__) #define DECLARE_ARGUMENTS(...) RX_FOREACH_COMMA(_, TYPE_AND_NAME_CAT, ## __VA_ARGS__) // optimized observe methods #define GENERATE_METHOD_IDENTIFIER(...) RX_CAT2(swizzle, RX_FOREACH(_, CAT, UNDERSCORE_TYPE_CAT, ## __VA_ARGS__)) #define GENERATE_OBSERVE_METHOD_DECLARATION(...) \ -(BOOL)GENERATE_METHOD_IDENTIFIER(__VA_ARGS__):(Class __nonnull)class \ selector:(SEL)selector \ error:(NSErrorParam)error { \ #define BUILD_EXAMPLE_METHOD(return_value, ...) \ +(return_value)RX_CAT2(RX_CAT2(example_, return_value), RX_FOREACH(_, SEPARATE_BY_SPACE, EXAMPLE_PARAMETER, ## __VA_ARGS__)) {} #define BUILD_EXAMPLE_METHOD_SELECTOR(return_value, ...) \ RX_CAT2(RX_CAT2(example_, return_value), RX_FOREACH(_, SEPARATE_BY_SPACE, SELECTOR_PART, ## __VA_ARGS__)) #define SWIZZLE_OBSERVE_METHOD(return_value, ...) \ @interface RXObjCRuntime (GENERATE_METHOD_IDENTIFIER(return_value, ## __VA_ARGS__)) \ @end \ \ @implementation RXObjCRuntime(GENERATE_METHOD_IDENTIFIER(return_value, ## __VA_ARGS__)) \ BUILD_EXAMPLE_METHOD(return_value, ## __VA_ARGS__) \ SWIZZLE_METHOD(return_value, GENERATE_OBSERVE_METHOD_DECLARATION(return_value, ## __VA_ARGS__), OBSERVE_BODY, OBSERVE_INVOKED_BODY, ## __VA_ARGS__) \ \ +(void)load { \ __unused SEL exampleSelector = @selector(BUILD_EXAMPLE_METHOD_SELECTOR(return_value, ## __VA_ARGS__)); \ [self registerOptimizedObserver:^BOOL(RXObjCRuntime * __nonnull self, Class __nonnull class, \ SEL __nonnull selector, NSErrorParam error) { \ return [self GENERATE_METHOD_IDENTIFIER(return_value, ## __VA_ARGS__):class selector:selector error:error]; \ } encodedAs:exampleSelector]; \ } \ \ @end \ // infrastructure method #define NO_BODY(...) #define SWIZZLE_INFRASTRUCTURE_METHOD(return_value, method_name, parameters, method_selector, body, ...) \ SWIZZLE_METHOD(return_value, -(BOOL)method_name:(Class __nonnull)class parameters error:(NSErrorParam)error \ { \ SEL selector = method_selector; , body, NO_BODY, __VA_ARGS__) \ // common base #define SWIZZLE_METHOD(return_value, method_prototype, body, invoked_body, ...) \ method_prototype \ __unused SEL rxSelector = RX_selector(selector); \ IMP (^newImplementationGenerator)(void) = ^() { \ __block IMP thisIMP = nil; \ id newImplementation = ^return_value(__unsafe_unretained id self DECLARE_ARGUMENTS(__VA_ARGS__)) { \ body(__VA_ARGS__) \ \ struct objc_super superInfo = { \ .receiver = self, \ .super_class = class_getSuperclass(class) \ }; \ \ return_value (*msgSend)(struct objc_super *, SEL DECLARE_ARGUMENTS(__VA_ARGS__)) \ = (__typeof__(msgSend))objc_msgSendSuper; \ @try { \ return msgSend(&superInfo, selector ARGUMENTS(__VA_ARGS__)); \ } \ @finally { invoked_body(__VA_ARGS__) } \ }; \ \ thisIMP = imp_implementationWithBlock(newImplementation); \ return thisIMP; \ }; \ \ IMP (^replacementImplementationGenerator)(IMP) = ^(IMP originalImplementation) { \ __block return_value (*originalImplementationTyped)(__unsafe_unretained id, SEL DECLARE_ARGUMENTS(__VA_ARGS__) ) \ = (__typeof__(originalImplementationTyped))(originalImplementation); \ \ __block IMP thisIMP = nil; \ id implementationReplacement = ^return_value(__unsafe_unretained id self DECLARE_ARGUMENTS(__VA_ARGS__) ) { \ body(__VA_ARGS__) \ @try { \ return originalImplementationTyped(self, selector ARGUMENTS(__VA_ARGS__)); \ } \ @finally { invoked_body(__VA_ARGS__) } \ }; \ \ thisIMP = imp_implementationWithBlock(implementationReplacement); \ return thisIMP; \ }; \ \ return [self ensureSwizzledSelector:selector \ ofClass:class \ newImplementationGenerator:newImplementationGenerator \ replacementImplementationGenerator:replacementImplementationGenerator \ error:error]; \ } \ @interface RXObjCRuntime (InfrastructureMethods) @end // MARK: Infrastructure Methods @implementation RXObjCRuntime (InfrastructureMethods) SWIZZLE_INFRASTRUCTURE_METHOD( void, swizzleForwardInvocation, , @selector(forwardInvocation:), FORWARD_BODY, NSInvocationRef ) SWIZZLE_INFRASTRUCTURE_METHOD( BOOL, swizzleRespondsToSelector, , @selector(respondsToSelector:), RESPONDS_TO_SELECTOR_BODY, SEL ) SWIZZLE_INFRASTRUCTURE_METHOD( Class __nonnull, swizzleClass, toActAs:(Class)actAsClass, @selector(class), CLASS_BODY ) SWIZZLE_INFRASTRUCTURE_METHOD( NSMethodSignatureRef, swizzleMethodSignatureForSelector, , @selector(methodSignatureForSelector:), METHOD_SIGNATURE_FOR_SELECTOR_BODY, SEL ) SWIZZLE_INFRASTRUCTURE_METHOD( void, swizzleDeallocating, , deallocSelector, DEALLOCATING_BODY ) @end // MARK: Optimized intercepting methods for specific combination of parameter types SWIZZLE_OBSERVE_METHOD(void) SWIZZLE_OBSERVE_METHOD(void, id) SWIZZLE_OBSERVE_METHOD(void, char) SWIZZLE_OBSERVE_METHOD(void, short) SWIZZLE_OBSERVE_METHOD(void, int) SWIZZLE_OBSERVE_METHOD(void, long) SWIZZLE_OBSERVE_METHOD(void, rx_uchar) SWIZZLE_OBSERVE_METHOD(void, rx_ushort) SWIZZLE_OBSERVE_METHOD(void, rx_uint) SWIZZLE_OBSERVE_METHOD(void, rx_ulong) SWIZZLE_OBSERVE_METHOD(void, rx_block) SWIZZLE_OBSERVE_METHOD(void, float) SWIZZLE_OBSERVE_METHOD(void, double) SWIZZLE_OBSERVE_METHOD(void, SEL) SWIZZLE_OBSERVE_METHOD(void, id, id) SWIZZLE_OBSERVE_METHOD(void, id, char) SWIZZLE_OBSERVE_METHOD(void, id, short) SWIZZLE_OBSERVE_METHOD(void, id, int) SWIZZLE_OBSERVE_METHOD(void, id, long) SWIZZLE_OBSERVE_METHOD(void, id, rx_uchar) SWIZZLE_OBSERVE_METHOD(void, id, rx_ushort) SWIZZLE_OBSERVE_METHOD(void, id, rx_uint) SWIZZLE_OBSERVE_METHOD(void, id, rx_ulong) SWIZZLE_OBSERVE_METHOD(void, id, rx_block) SWIZZLE_OBSERVE_METHOD(void, id, float) SWIZZLE_OBSERVE_METHOD(void, id, double) SWIZZLE_OBSERVE_METHOD(void, id, SEL) // MARK: RXObjCRuntime @implementation RXObjCRuntime static RXObjCRuntime *_instance = nil; static NSMutableDictionary *optimizedObserversByMethodEncoding = nil; +(RXObjCRuntime*)instance { return _instance; } +(void)initialize { _instance = [[RXObjCRuntime alloc] init]; defaultTypeID = CFGetTypeID((CFTypeRef)RXObjCRuntime.class); // just need a reference of some object not from CF deallocSelector = NSSelectorFromString(@"dealloc"); NSAssert(_instance != nil, @"Failed to initialize swizzling"); } -(instancetype)init { self = [super init]; if (!self) return nil; self.classesThatSupportObservingByForwarding = [NSMutableSet set]; self.forwardedSelectorsByClass = [NSMutableDictionary dictionary]; self.dynamicSubclassByRealClass = [NSMutableDictionary dictionary]; self.interceptorIMPbySelectorsByClass = [NSMutableDictionary dictionary]; pthread_mutexattr_t lock_attr; pthread_mutexattr_init(&lock_attr); pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&_lock, &lock_attr); pthread_mutexattr_destroy(&lock_attr); return self; } -(void)performLocked:(void (^)(RXObjCRuntime* __nonnull))action { pthread_mutex_lock(&_lock); action(self); pthread_mutex_unlock(&_lock); } +(void)registerOptimizedObserver:(RXInterceptWithOptimizedObserver)registration encodedAs:(SEL)selector { Method exampleEncodingMethod = class_getClassMethod(self, selector); ALWAYS(exampleEncodingMethod != nil, @"Example encoding method is nil."); NSString *methodEncoding = RX_method_encoding(exampleEncodingMethod); if (optimizedObserversByMethodEncoding == nil) { optimizedObserversByMethodEncoding = [NSMutableDictionary dictionary]; } DLOG(@"Added optimized method: %@ (%@)", methodEncoding, NSStringFromSelector(selector)); ALWAYS(optimizedObserversByMethodEncoding[methodEncoding] == nil, @"Optimized observer already registered") optimizedObserversByMethodEncoding[methodEncoding] = registration; } /** This is the main entry point for observing messages sent to arbitrary objects. */ -(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull)selector error:(NSErrorParam)error { Method instanceMethod = class_getInstanceMethod([target class], selector); if (instanceMethod == nil) { RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorSelectorNotImplemented userInfo:nil], nil); } if (selector == @selector(class) || selector == @selector(forwardingTargetForSelector:) || selector == @selector(methodSignatureForSelector:) || selector == @selector(respondsToSelector:)) { RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorObservingPerformanceSensitiveMessages userInfo:nil], nil); } // For `dealloc` message, original implementation will be swizzled. // This is a special case because observing `dealloc` message is performed when `observeWeakly` is used. // // Some toll free bridged classes don't handle `object_setClass` well and cause crashes. // // To make `deallocating` as robust as possible, original implementation will be replaced. if (selector == deallocSelector) { Class __nonnull deallocSwizzingTarget = [target class]; IMP interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:deallocSwizzingTarget]; if (interceptorIMPForSelector != nil) { return interceptorIMPForSelector; } if (![self swizzleDeallocating:deallocSwizzingTarget error:error]) { return nil; } interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:deallocSwizzingTarget]; if (interceptorIMPForSelector != nil) { return interceptorIMPForSelector; } } else { Class __nullable swizzlingImplementorClass = [self prepareTargetClassForObserving:target error:error]; if (swizzlingImplementorClass == nil) { return nil; } NSString *methodEncoding = RX_method_encoding(instanceMethod); RXInterceptWithOptimizedObserver optimizedIntercept = optimizedObserversByMethodEncoding[methodEncoding]; if (!RX_method_has_supported_return_type(instanceMethod)) { RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorObservingMessagesWithUnsupportedReturnType userInfo:nil], nil); } // optimized interception method if (optimizedIntercept != nil) { IMP interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:swizzlingImplementorClass]; if (interceptorIMPForSelector != nil) { return interceptorIMPForSelector; } if (!optimizedIntercept(self, swizzlingImplementorClass, selector, error)) { return nil; } interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:swizzlingImplementorClass]; if (interceptorIMPForSelector != nil) { return interceptorIMPForSelector; } } // default fallback to observing by forwarding messages else { if ([self forwardingSelector:selector forClass:swizzlingImplementorClass]) { return RX_default_target_implementation(); } if (![self observeByForwardingMessages:swizzlingImplementorClass selector:selector target:target error:error]) { return nil; } if ([self forwardingSelector:selector forClass:swizzlingImplementorClass]) { return RX_default_target_implementation(); } } } RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorUnknown userInfo:nil], nil); } -(Class __nullable)prepareTargetClassForObserving:(id __nonnull)target error:(NSErrorParam)error { Class swizzlingClass = objc_getAssociatedObject(target, &RxSwizzlingTargetClassKey); if (swizzlingClass != nil) { return swizzlingClass; } Class __nonnull wannaBeClass = [target class]; /** Core Foundation classes are usually toll free bridged. Those classes crash the program in case `object_setClass` is performed on them. There is a possibility to just swizzle methods on original object, but since those won't be usual use cases for this library, then an error will just be reported for now. */ BOOL isThisTollFreeFoundationClass = CFGetTypeID((CFTypeRef)target) != defaultTypeID; if (isThisTollFreeFoundationClass) { RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorCantInterceptCoreFoundationTollFreeBridgedObjects userInfo:nil], nil); } /** If the object is reporting a different class then what it's real class, that means that there is probably already some interception mechanism in place or something weird is happening. Most common case when this would happen is when using KVO (`observe`) and `sentMessage`. This error is easily resolved by just using `sentMessage` observing before `observe`. The reason why other way around could create issues is because KVO will unregister it's interceptor class and restore original class. Unfortunately that will happen no matter was there another interceptor subclass registered in hierarchy or not. Failure scenario: * KVO sets class to be `__KVO__OriginalClass` (subclass of `OriginalClass`) * `sentMessage` sets object class to be `_RX_namespace___KVO__OriginalClass` (subclass of `__KVO__OriginalClass`) * then unobserving with KVO will restore class to be `OriginalClass` -> failure point The reason why changing order of observing works is because any interception method should return object's original real class (if that doesn't happen then it's really easy to argue that's a bug in that other library). This library won't remove registered interceptor even if there aren't any observers left because it's highly unlikely it would have any benefit in real world use cases, and it's even more dangerous. */ if ([target class] != object_getClass(target)) { BOOL isKVO = [target respondsToSelector:NSSelectorFromString(@"_isKVOA")]; RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorObjectMessagesAlreadyBeingIntercepted userInfo:@{ RXObjCRuntimeErrorIsKVOKey : @(isKVO) }], nil); } Class __nullable dynamicFakeSubclass = [self ensureHasDynamicFakeSubclass:wannaBeClass error:error]; if (dynamicFakeSubclass == nil) { return nil; } Class previousClass = object_setClass(target, dynamicFakeSubclass); if (previousClass != wannaBeClass) { THREADING_HAZARD(wannaBeClass); RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorThreadingCollisionWithOtherInterceptionMechanism userInfo:nil], nil); } objc_setAssociatedObject(target, &RxSwizzlingTargetClassKey, dynamicFakeSubclass, OBJC_ASSOCIATION_RETAIN_NONATOMIC); return dynamicFakeSubclass; } -(BOOL)forwardingSelector:(SEL)selector forClass:(Class __nonnull)class { return [self.forwardedSelectorsByClass[CLASS_VALUE(class)] containsObject:SEL_VALUE(selector)]; } -(void)registerForwardedSelector:(SEL)selector forClass:(Class __nonnull)class { NSValue *classValue = CLASS_VALUE(class); NSMutableSet *forwardedSelectors = self.forwardedSelectorsByClass[classValue]; if (forwardedSelectors == nil) { forwardedSelectors = [NSMutableSet set]; self.forwardedSelectorsByClass[classValue] = forwardedSelectors; } [forwardedSelectors addObject:SEL_VALUE(selector)]; } -(BOOL)observeByForwardingMessages:(Class __nonnull)swizzlingImplementorClass selector:(SEL)selector target:(id __nonnull)target error:(NSErrorParam)error { if (![self ensureForwardingMethodsAreSwizzled:swizzlingImplementorClass error:error]) { return NO; } ALWAYS(![self forwardingSelector:selector forClass:swizzlingImplementorClass], @"Already observing selector for class"); #if TRACE_RESOURCES OSAtomicIncrement32Barrier(&numberOfForwardedMethods); #endif SEL rxSelector = RX_selector(selector); Method instanceMethod = class_getInstanceMethod(swizzlingImplementorClass, selector); ALWAYS(instanceMethod != nil, @"Instance method is nil"); const char* methodEncoding = method_getTypeEncoding(instanceMethod); ALWAYS(methodEncoding != nil, @"Method encoding is nil."); NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:methodEncoding]; ALWAYS(methodSignature != nil, @"Method signature is invalid."); IMP implementation = method_getImplementation(instanceMethod); if (implementation == nil) { RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorSelectorNotImplemented userInfo:nil], NO); } if (!class_addMethod(swizzlingImplementorClass, rxSelector, implementation, methodEncoding)) { RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorSavingOriginalForwardingMethodFailed userInfo:nil], NO); } if (!class_addMethod(swizzlingImplementorClass, selector, _objc_msgForward, methodEncoding)) { if (implementation != method_setImplementation(instanceMethod, _objc_msgForward)) { THREADING_HAZARD(swizzlingImplementorClass); RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain code:RXObjCRuntimeErrorReplacingMethodWithForwardingImplementation userInfo:nil], NO); } } DLOG(@"Rx uses forwarding to observe `%@` for `%@`.", NSStringFromSelector(selector), swizzlingImplementorClass); [self registerForwardedSelector:selector forClass:swizzlingImplementorClass]; return YES; } /** If object don't have some weird behavior, claims it's the same class that runtime shows, then dynamic subclass is created (only this instance will have performance hit). In case something weird is detected, then original base class is being swizzled and all instances will have somewhat reduced performance. This is especially handy optimization for weak KVO. Nobody will swizzle for example `NSString`, but to know when instance of a `NSString` was deallocated, performance hit will be only felt on a single instance of `NSString`, not all instances of `NSString`s. */ -(Class __nullable)ensureHasDynamicFakeSubclass:(Class __nonnull)class error:(NSErrorParam)error { Class dynamicFakeSubclass = self.dynamicSubclassByRealClass[CLASS_VALUE(class)]; if (dynamicFakeSubclass != nil) { return dynamicFakeSubclass; } NSString *dynamicFakeSubclassName = [RX_PREFIX stringByAppendingString:NSStringFromClass(class)]; const char *dynamicFakeSubclassNameRaw = dynamicFakeSubclassName.UTF8String; dynamicFakeSubclass = objc_allocateClassPair(class, dynamicFakeSubclassNameRaw, 0); ALWAYS(dynamicFakeSubclass != nil, @"Class not generated"); if (![self swizzleClass:dynamicFakeSubclass toActAs:class error:error]) { return nil; } objc_registerClassPair(dynamicFakeSubclass); [self.dynamicSubclassByRealClass setObject:dynamicFakeSubclass forKey:CLASS_VALUE(class)]; ALWAYS(self.dynamicSubclassByRealClass[CLASS_VALUE(class)] != nil, @"Class not registered"); return dynamicFakeSubclass; } -(BOOL)ensureForwardingMethodsAreSwizzled:(Class __nonnull)class error:(NSErrorParam)error { NSValue *classValue = CLASS_VALUE(class); if ([self.classesThatSupportObservingByForwarding containsObject:classValue]) { return YES; } if (![self swizzleForwardInvocation:class error:error]) { return NO; } if (![self swizzleMethodSignatureForSelector:class error:error]) { return NO; } if (![self swizzleRespondsToSelector:class error:error]) { return NO; } [self.classesThatSupportObservingByForwarding addObject:classValue]; return YES; } -(void)registerInterceptedSelector:(SEL)selector implementation:(IMP)implementation forClass:(Class)class { NSValue * __nonnull classValue = CLASS_VALUE(class); NSValue * __nonnull selectorValue = SEL_VALUE(selector); NSMutableDictionary *swizzledIMPBySelectorsForClass = self.interceptorIMPbySelectorsByClass[classValue]; if (swizzledIMPBySelectorsForClass == nil) { swizzledIMPBySelectorsForClass = [NSMutableDictionary dictionary]; self.interceptorIMPbySelectorsByClass[classValue] = swizzledIMPBySelectorsForClass; } swizzledIMPBySelectorsForClass[selectorValue] = IMP_VALUE(implementation); ALWAYS([self interceptorImplementationForSelector:selector forClass:class] != nil, @"Class should have been swizzled"); } -(IMP)interceptorImplementationForSelector:(SEL)selector forClass:(Class)class { NSValue * __nonnull classValue = CLASS_VALUE(class); NSValue * __nonnull selectorValue = SEL_VALUE(selector); NSMutableDictionary *swizzledIMPBySelectorForClass = self.interceptorIMPbySelectorsByClass[classValue]; NSValue *impValue = swizzledIMPBySelectorForClass[selectorValue]; return impValue.pointerValue; } -(BOOL)ensureSwizzledSelector:(SEL __nonnull)selector ofClass:(Class __nonnull)class newImplementationGenerator:(IMP(^)(void))newImplementationGenerator replacementImplementationGenerator:(IMP (^)(IMP originalImplementation))replacementImplementationGenerator error:(NSErrorParam)error { if ([self interceptorImplementationForSelector:selector forClass:class] != nil) { DLOG(@"Trying to register same intercept at least once, this sounds like a possible bug"); return YES; } #if TRACE_RESOURCES OSAtomicIncrement32Barrier(&numberOInterceptedMethods); #endif DLOG(@"Rx is swizzling `%@` for `%@`", NSStringFromSelector(selector), class); Method existingMethod = class_getInstanceMethod(class, selector); ALWAYS(existingMethod != nil, @"Method doesn't exist"); const char *encoding = method_getTypeEncoding(existingMethod); ALWAYS(encoding != nil, @"Encoding is nil"); IMP newImplementation = newImplementationGenerator(); if (class_addMethod(class, selector, newImplementation, encoding)) { // new method added, job done [self registerInterceptedSelector:selector implementation:newImplementation forClass:class]; return YES; } imp_removeBlock(newImplementation); // if add fails, that means that method already exists on targetClass Method existingMethodOnTargetClass = existingMethod; IMP originalImplementation = method_getImplementation(existingMethodOnTargetClass); ALWAYS(originalImplementation != nil, @"Method must exist."); IMP implementationReplacementIMP = replacementImplementationGenerator(originalImplementation); ALWAYS(implementationReplacementIMP != nil, @"Method must exist."); IMP originalImplementationAfterChange = method_setImplementation(existingMethodOnTargetClass, implementationReplacementIMP); ALWAYS(originalImplementation != nil, @"Method must exist."); // If method replacing failed, who knows what happened, better not trying again, otherwise program can get // corrupted. [self registerInterceptedSelector:selector implementation:implementationReplacementIMP forClass:class]; // ¯\_(ツ)_/¯ if (originalImplementationAfterChange != originalImplementation) { THREADING_HAZARD(class); return NO; } return YES; } @end #if TRACE_RESOURCES NSInteger RX_number_of_dynamic_subclasses() { __block NSInteger count = 0; [[RXObjCRuntime instance] performLocked:^(RXObjCRuntime * __nonnull self) { count = self.dynamicSubclassByRealClass.count; }]; return count; } NSInteger RX_number_of_forwarding_enabled_classes() { __block NSInteger count = 0; [[RXObjCRuntime instance] performLocked:^(RXObjCRuntime * __nonnull self) { count = self.classesThatSupportObservingByForwarding.count; }]; return count; } NSInteger RX_number_of_intercepting_classes() { __block NSInteger count = 0; [[RXObjCRuntime instance] performLocked:^(RXObjCRuntime * __nonnull self) { count = self.interceptorIMPbySelectorsByClass.count; }]; return count; } NSInteger RX_number_of_forwarded_methods() { return numberOfForwardedMethods; } NSInteger RX_number_of_swizzled_methods() { return numberOInterceptedMethods; } #endif #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/include/RxCocoaRuntime.h ================================================ // // RxCocoaRuntime.h // RxCocoa // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import #import "_RX.h" #import "_RXDelegateProxy.h" #import "_RXKVOObserver.h" #import "_RXObjCRuntime.h" //! Project version number for RxCocoa. FOUNDATION_EXPORT double RxCocoaVersionNumber; //! Project version string for RxCocoa. FOUNDATION_EXPORT const unsigned char RxCocoaVersionString[]; ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/include/_RX.h ================================================ // // _RX.h // RxCocoa // // Created by Krunoslav Zaher on 7/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import #import /** ################################################################################ This file is part of RX private API ################################################################################ */ #if TRACE_RESOURCES >= 2 # define DLOG(...) NSLog(__VA_ARGS__) #else # define DLOG(...) #endif #if DEBUG # define ABORT_IN_DEBUG abort(); #else # define ABORT_IN_DEBUG #endif #define SEL_VALUE(x) [NSValue valueWithPointer:(x)] #define CLASS_VALUE(x) [NSValue valueWithNonretainedObject:(x)] #define IMP_VALUE(x) [NSValue valueWithPointer:(x)] /** Checks that the local `error` instance exists before assigning it's value by reference. This macro exists to work around static analysis warnings — `NSError` is always assumed to be `nullable`, even though we explicitly define the method parameter as `nonnull`. See http://www.openradar.me/21766176 for more details. */ #define RX_THROW_ERROR(errorValue, returnValue) if (error != nil) { *error = (errorValue); } return (returnValue); #define RX_CAT2(_1, _2) _RX_CAT2(_1, _2) #define _RX_CAT2(_1, _2) _1 ## _2 #define RX_ELEMENT_AT(n, ...) RX_CAT2(_RX_ELEMENT_AT_, n)(__VA_ARGS__) #define _RX_ELEMENT_AT_0(x, ...) x #define _RX_ELEMENT_AT_1(_0, x, ...) x #define _RX_ELEMENT_AT_2(_0, _1, x, ...) x #define _RX_ELEMENT_AT_3(_0, _1, _2, x, ...) x #define _RX_ELEMENT_AT_4(_0, _1, _2, _3, x, ...) x #define _RX_ELEMENT_AT_5(_0, _1, _2, _3, _4, x, ...) x #define _RX_ELEMENT_AT_6(_0, _1, _2, _3, _4, _5, x, ...) x #define RX_COUNT(...) RX_ELEMENT_AT(6, ## __VA_ARGS__, 6, 5, 4, 3, 2, 1, 0) #define RX_EMPTY(...) RX_ELEMENT_AT(6, ## __VA_ARGS__, 0, 0, 0, 0, 0, 0, 1) /** #define SUM(context, index, head, tail) head + tail #define MAP(context, index, element) (context)[index] * (element) RX_FOR(numbers, SUM, MAP, b0, b1, b2); (numbers)[0] * (b0) + (numbers)[1] * (b1) + (numbers[2]) * (b2) */ #define RX_FOREACH(context, concat, map, ...) RX_FOR_MAX(RX_COUNT(__VA_ARGS__), _RX_FOREACH_CONCAT, _RX_FOREACH_MAP, context, concat, map, __VA_ARGS__) #define _RX_FOREACH_CONCAT(index, head, tail, context, concat, map, ...) concat(context, index, head, tail) #define _RX_FOREACH_MAP(index, context, concat, map, ...) map(context, index, RX_ELEMENT_AT(index, __VA_ARGS__)) /** #define MAP(context, index, item) (context)[index] * (item) RX_FOR_COMMA(numbers, MAP, b0, b1); ,(numbers)[0] * b0, (numbers)[1] * b1 */ #define RX_FOREACH_COMMA(context, map, ...) RX_CAT2(_RX_FOREACH_COMMA_EMPTY_, RX_EMPTY(__VA_ARGS__))(context, map, ## __VA_ARGS__) #define _RX_FOREACH_COMMA_EMPTY_1(context, map, ...) #define _RX_FOREACH_COMMA_EMPTY_0(context, map, ...) , RX_FOR_MAX(RX_COUNT(__VA_ARGS__), _RX_FOREACH_COMMA_CONCAT, _RX_FOREACH_COMMA_MAP, context, map, __VA_ARGS__) #define _RX_FOREACH_COMMA_CONCAT(index, head, tail, context, map, ...) head, tail #define _RX_FOREACH_COMMA_MAP(index, context, map, ...) map(context, index, RX_ELEMENT_AT(index, __VA_ARGS__)) // rx for #define RX_FOR_MAX(max, concat, map, ...) RX_CAT2(RX_FOR_, max)(concat, map, ## __VA_ARGS__) #define RX_FOR_0(concat, map, ...) #define RX_FOR_1(concat, map, ...) map(0, __VA_ARGS__) #define RX_FOR_2(concat, map, ...) concat(1, RX_FOR_1(concat, map, ## __VA_ARGS__), map(1, __VA_ARGS__), __VA_ARGS__) #define RX_FOR_3(concat, map, ...) concat(2, RX_FOR_2(concat, map, ## __VA_ARGS__), map(2, __VA_ARGS__), __VA_ARGS__) #define RX_FOR_4(concat, map, ...) concat(3, RX_FOR_3(concat, map, ## __VA_ARGS__), map(3, __VA_ARGS__), __VA_ARGS__) #define RX_FOR_5(concat, map, ...) concat(4, RX_FOR_4(concat, map, ## __VA_ARGS__), map(4, __VA_ARGS__), __VA_ARGS__) #define RX_FOR_6(concat, map, ...) concat(5, RX_FOR_5(concat, map, ## __VA_ARGS__), map(5, __VA_ARGS__), __VA_ARGS__) ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/include/_RXDelegateProxy.h ================================================ // // _RXDelegateProxy.h // RxCocoa // // Created by Krunoslav Zaher on 7/4/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface _RXDelegateProxy : NSObject @property (nonatomic, weak, readonly) id _forwardToDelegate; -(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate NS_SWIFT_NAME(_setForwardToDelegate(_:retainDelegate:)) ; -(BOOL)hasWiredImplementationForSelector:(SEL)selector; -(BOOL)voidDelegateMethodsContain:(SEL)selector; -(void)_sentMessage:(SEL)selector withArguments:(NSArray*)arguments; -(void)_methodInvoked:(SEL)selector withArguments:(NSArray*)arguments; @end NS_ASSUME_NONNULL_END ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/include/_RXKVOObserver.h ================================================ // // _RXKVOObserver.h // RxCocoa // // Created by Krunoslav Zaher on 7/11/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import /** ################################################################################ This file is part of RX private API ################################################################################ */ // Exists because if written in Swift, reading unowned is disabled during dealloc process @interface _RXKVOObserver : NSObject -(instancetype)initWithTarget:(id)target retainTarget:(BOOL)retainTarget keyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options callback:(void (^)(id))callback; -(void)dispose; @end ================================================ FILE: Pods/RxCocoa/RxCocoa/Runtime/include/_RXObjCRuntime.h ================================================ // // _RXObjCRuntime.h // RxCocoa // // Created by Krunoslav Zaher on 7/11/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import #if !DISABLE_SWIZZLING /** ################################################################################ This file is part of RX private API ################################################################################ */ /** This flag controls `RELEASE` configuration behavior in case race was detecting while modifying ObjC runtime. In case this value is set to `YES`, after runtime race is detected, `abort()` will be called. Otherwise, only error will be reported using normal error reporting mechanism. In `DEBUG` mode `abort` will be always called in case race is detected. Races can't happen in case this is the only library modifying ObjC runtime, but in case there are multiple libraries changing ObjC runtime, race conditions can occur because there is no way to synchronize multiple libraries unaware of each other. To help remedy this situation this library will use `synchronized` on target object and it's meta-class, but there aren't any guarantees of how other libraries will behave. Default value is `NO`. */ extern BOOL RXAbortOnThreadingHazard; /// Error domain for RXObjCRuntime. extern NSString * __nonnull const RXObjCRuntimeErrorDomain; /// `userInfo` key with additional information is interceptor probably KVO. extern NSString * __nonnull const RXObjCRuntimeErrorIsKVOKey; typedef NS_ENUM(NSInteger, RXObjCRuntimeError) { RXObjCRuntimeErrorUnknown = 1, RXObjCRuntimeErrorObjectMessagesAlreadyBeingIntercepted = 2, RXObjCRuntimeErrorSelectorNotImplemented = 3, RXObjCRuntimeErrorCantInterceptCoreFoundationTollFreeBridgedObjects = 4, RXObjCRuntimeErrorThreadingCollisionWithOtherInterceptionMechanism = 5, RXObjCRuntimeErrorSavingOriginalForwardingMethodFailed = 6, RXObjCRuntimeErrorReplacingMethodWithForwardingImplementation = 7, RXObjCRuntimeErrorObservingPerformanceSensitiveMessages = 8, RXObjCRuntimeErrorObservingMessagesWithUnsupportedReturnType = 9, }; /// Transforms normal selector into a selector with RX prefix. SEL _Nonnull RX_selector(SEL _Nonnull selector); /// Transforms selector into a unique pointer (because of Swift conversion rules) void * __nonnull RX_reference_from_selector(SEL __nonnull selector); /// Protocol that interception observers must implement. @protocol RXMessageSentObserver /// In case the same selector is being intercepted for a pair of base/sub classes, /// this property will differentiate between interceptors that need to fire. @property (nonatomic, assign, readonly) IMP __nonnull targetImplementation; -(void)messageSentWithArguments:(NSArray* __nonnull)arguments; -(void)methodInvokedWithArguments:(NSArray* __nonnull)arguments; @end /// Protocol that deallocating observer must implement. @protocol RXDeallocatingObserver /// In case the same selector is being intercepted for a pair of base/sub classes, /// this property will differentiate between interceptors that need to fire. @property (nonatomic, assign, readonly) IMP __nonnull targetImplementation; -(void)deallocating; @end /// Ensures interceptor is installed on target object. IMP __nullable RX_ensure_observing(id __nonnull target, SEL __nonnull selector, NSError *__autoreleasing __nullable * __nullable error); /// Extracts arguments for `invocation`. NSArray * __nonnull RX_extract_arguments(NSInvocation * __nonnull invocation); /// Returns `YES` in case method has `void` return type. BOOL RX_is_method_with_description_void(struct objc_method_description method); /// Returns `YES` in case methodSignature has `void` return type. BOOL RX_is_method_signature_void(NSMethodSignature * __nonnull methodSignature); /// Default value for `RXInterceptionObserver.targetImplementation`. IMP __nonnull RX_default_target_implementation(void); #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/RxCocoa.h ================================================ // // RxCocoa.h // RxCocoa // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #import #import "_RX.h" #import "_RXDelegateProxy.h" #import "_RXKVOObserver.h" #import "_RXObjCRuntime.h" //! Project version number for RxCocoa. FOUNDATION_EXPORT double RxCocoaVersionNumber; //! Project version string for RxCocoa. FOUNDATION_EXPORT const unsigned char RxCocoaVersionString[]; ================================================ FILE: Pods/RxCocoa/RxCocoa/RxCocoa.swift ================================================ // // RxCocoa.swift // RxCocoa // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import class Foundation.NSNull #if !RX_NO_MODULE import RxSwift #endif #if os(iOS) import UIKit #endif /// RxCocoa errors. public enum RxCocoaError : Swift.Error , CustomDebugStringConvertible { /// Unknown error has occurred. case unknown /// Invalid operation was attempted. case invalidOperation(object: Any) /// Items are not yet bound to user interface but have been requested. case itemsNotYetBound(object: Any) /// Invalid KVO Path. case invalidPropertyName(object: Any, propertyName: String) /// Invalid object on key path. case invalidObjectOnKeyPath(object: Any, sourceObject: AnyObject, propertyName: String) /// Error during swizzling. case errorDuringSwizzling /// Casting error. case castingError(object: Any, targetType: Any.Type) } // MARK: Debug descriptions extension RxCocoaError { /// A textual representation of `self`, suitable for debugging. public var debugDescription: String { switch self { case .unknown: return "Unknown error occurred." case let .invalidOperation(object): return "Invalid operation was attempted on `\(object)`." case let .itemsNotYetBound(object): return "Data source is set, but items are not yet bound to user interface for `\(object)`." case let .invalidPropertyName(object, propertyName): return "Object `\(object)` doesn't have a property named `\(propertyName)`." case let .invalidObjectOnKeyPath(object, sourceObject, propertyName): return "Unobservable object `\(object)` was observed as `\(propertyName)` of `\(sourceObject)`." case .errorDuringSwizzling: return "Error during swizzling." case .castingError(let object, let targetType): return "Error casting `\(object)` to `\(targetType)`" } } } // MARK: Error binding policies func bindingError(_ error: Swift.Error) { let error = "Binding error: \(error)" #if DEBUG rxFatalError(error) #else print(error) #endif } /// Swift does not implement abstract methods. This method is used as a runtime check to ensure that methods which intended to be abstract (i.e., they should be implemented in subclasses) are not called directly on the superclass. func rxAbstractMethod(message: String = "Abstract method", file: StaticString = #file, line: UInt = #line) -> Swift.Never { rxFatalError(message, file: file, line: line) } func rxFatalError(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> Swift.Never { // The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours. fatalError(lastMessage(), file: file, line: line) } func rxFatalErrorInDebug(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) { #if DEBUG fatalError(lastMessage(), file: file, line: line) #else print("\(file):\(line): \(lastMessage())") #endif } // MARK: casts or fatal error // workaround for Swift compiler bug, cheers compiler team :) func castOptionalOrFatalError(_ value: Any?) -> T? { if value == nil { return nil } let v: T = castOrFatalError(value) return v } func castOrThrow(_ resultType: T.Type, _ object: Any) throws -> T { guard let returnValue = object as? T else { throw RxCocoaError.castingError(object: object, targetType: resultType) } return returnValue } func castOptionalOrThrow(_ resultType: T.Type, _ object: AnyObject) throws -> T? { if NSNull().isEqual(object) { return nil } guard let returnValue = object as? T else { throw RxCocoaError.castingError(object: object, targetType: resultType) } return returnValue } func castOrFatalError(_ value: AnyObject!, message: String) -> T { let maybeResult: T? = value as? T guard let result = maybeResult else { rxFatalError(message) } return result } func castOrFatalError(_ value: Any!) -> T { let maybeResult: T? = value as? T guard let result = maybeResult else { rxFatalError("Failure converting from \(value) to \(T.self)") } return result } // MARK: Error messages let dataSourceNotSet = "DataSource not set" let delegateNotSet = "Delegate not set" // MARK: Shared with RxSwift #if !RX_NO_MODULE func rxFatalError(_ lastMessage: String) -> Never { // The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours. fatalError(lastMessage) } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/BehaviorRelay.swift ================================================ // // BehaviorRelay.swift // RxCocoa // // Created by Krunoslav Zaher on 10/7/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif /// BehaviorRelay is a wrapper for `BehaviorSubject`. /// /// Unlike `BehaviorSubject` it can't terminate with error or completed. public final class BehaviorRelay: ObservableType { public typealias E = Element private let _subject: BehaviorSubject // Accepts `event` and emits it to subscribers public func accept(_ event: Element) { _subject.onNext(event) } /// Current value of behavior subject public var value: Element { // this try! is ok because subject can't error out or be disposed return try! _subject.value() } /// Initializes variable with initial value. public init(value: Element) { _subject = BehaviorSubject(value: value) } /// Subscribes observer public func subscribe(_ observer: O) -> Disposable where O.E == E { return _subject.subscribe(observer) } /// - returns: Canonical interface for push style sequence public func asObservable() -> Observable { return _subject.asObservable() } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/ControlEvent.swift ================================================ // // ControlEvent.swift // RxCocoa // // Created by Krunoslav Zaher on 8/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif /// Protocol that enables extension of `ControlEvent`. public protocol ControlEventType : ObservableType { /// - returns: `ControlEvent` interface func asControlEvent() -> ControlEvent } /** Trait for `Observable`/`ObservableType` that represents event on UI element. It's properties are: - it never fails - it won't send any initial value on subscription - it will `Complete` sequence on control being deallocated - it never errors out - it delivers events on `MainScheduler.instance` **The implementation of `ControlEvent` will ensure that sequence of events is being subscribed on main scheduler (`subscribeOn(ConcurrentMainScheduler.instance)` behavior).** **It is implementor's responsibility to make sure that that all other properties enumerated above are satisfied.** **If they aren't, then using this trait communicates wrong properties and could potentially break someone's code.** **In case `events` observable sequence that is being passed into initializer doesn't satisfy all enumerated properties, please don't use this trait.** */ public struct ControlEvent : ControlEventType { public typealias E = PropertyType let _events: Observable /// Initializes control event with a observable sequence that represents events. /// /// - parameter events: Observable sequence that represents events. /// - returns: Control event created with a observable sequence of events. public init(events: Ev) where Ev.E == E { _events = events.subscribeOn(ConcurrentMainScheduler.instance) } /// Subscribes an observer to control events. /// /// - parameter observer: Observer to subscribe to events. /// - returns: Disposable object that can be used to unsubscribe the observer from receiving control events. public func subscribe(_ observer: O) -> Disposable where O.E == E { return _events.subscribe(observer) } /// - returns: `Observable` interface. public func asObservable() -> Observable { return _events } /// - returns: `ControlEvent` interface. public func asControlEvent() -> ControlEvent { return self } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/ControlProperty.swift ================================================ // // ControlProperty.swift // RxCocoa // // Created by Krunoslav Zaher on 8/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif /// Protocol that enables extension of `ControlProperty`. public protocol ControlPropertyType : ObservableType, ObserverType { /// - returns: `ControlProperty` interface func asControlProperty() -> ControlProperty } /** Trait for `Observable`/`ObservableType` that represents property of UI element. Sequence of values only represents initial control value and user initiated value changes. Programatic value changes won't be reported. It's properties are: - it never fails - `shareReplay(1)` behavior - it's stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced - it will `Complete` sequence on control being deallocated - it never errors out - it delivers events on `MainScheduler.instance` **The implementation of `ControlProperty` will ensure that sequence of values is being subscribed on main scheduler (`subscribeOn(ConcurrentMainScheduler.instance)` behavior).** **It is implementor's responsibility to make sure that that all other properties enumerated above are satisfied.** **If they aren't, then using this trait communicates wrong properties and could potentially break someone's code.** **In case `values` observable sequence that is being passed into initializer doesn't satisfy all enumerated properties, please don't use this trait.** */ public struct ControlProperty : ControlPropertyType { public typealias E = PropertyType let _values: Observable let _valueSink: AnyObserver /// Initializes control property with a observable sequence that represents property values and observer that enables /// binding values to property. /// /// - parameter values: Observable sequence that represents property values. /// - parameter valueSink: Observer that enables binding values to control property. /// - returns: Control property created with a observable sequence of values and an observer that enables binding values /// to property. public init(values: V, valueSink: S) where E == V.E, E == S.E { _values = values.subscribeOn(ConcurrentMainScheduler.instance) _valueSink = valueSink.asObserver() } /// Subscribes an observer to control property values. /// /// - parameter observer: Observer to subscribe to property values. /// - returns: Disposable object that can be used to unsubscribe the observer from receiving control property values. public func subscribe(_ observer: O) -> Disposable where O.E == E { return _values.subscribe(observer) } /// `ControlEvent` of user initiated value changes. Every time user updates control value change event /// will be emitted from `changed` event. /// /// Programatic changes to control value won't be reported. /// /// It contains all control property values except for first one. /// /// The name only implies that sequence element will be generated once user changes a value and not that /// adjacent sequence values need to be different (e.g. because of interaction between programatic and user updates, /// or for any other reason). public var changed: ControlEvent { get { return ControlEvent(events: _values.skip(1)) } } /// - returns: `Observable` interface. public func asObservable() -> Observable { return _values } /// - returns: `ControlProperty` interface. public func asControlProperty() -> ControlProperty { return self } /// Binds event to user interface. /// /// - In case next element is received, it is being set to control value. /// - In case error is received, DEBUG buids raise fatal error, RELEASE builds log event to standard output. /// - In case sequence completes, nothing happens. public func on(_ event: Event) { switch event { case .error(let error): bindingError(error) case .next: _valueSink.on(event) case .completed: _valueSink.on(event) } } } extension ControlPropertyType where E == String? { /// Transforms control property of type `String?` into control property of type `String`. public var orEmpty: ControlProperty { let original: ControlProperty = self.asControlProperty() let values: Observable = original._values.map { $0 ?? "" } let valueSink: AnyObserver = original._valueSink.mapObserver { $0 } return ControlProperty(values: values, valueSink: valueSink) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Driver/BehaviorRelay+Driver.swift ================================================ // // BehaviorRelay+Driver.swift // RxCocoa // // Created by Krunoslav Zaher on 10/7/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension BehaviorRelay { /// Converts `BehaviorRelay` to `Driver`. /// /// - returns: Observable sequence. public func asDriver() -> Driver { let source = self.asObservable() .observeOn(DriverSharingStrategy.scheduler) return SharedSequence(source) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Driver/ControlEvent+Driver.swift ================================================ // // ControlEvent+Driver.swift // RxCocoa // // Created by Krunoslav Zaher on 9/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension ControlEvent { /// Converts `ControlEvent` to `Driver` trait. /// /// `ControlEvent` already can't fail, so no special case needs to be handled. public func asDriver() -> Driver { return self.asDriver { (error) -> Driver in #if DEBUG rxFatalError("Somehow driver received error from a source that shouldn't fail.") #else return Driver.empty() #endif } } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Driver/ControlProperty+Driver.swift ================================================ // // ControlProperty+Driver.swift // RxCocoa // // Created by Krunoslav Zaher on 9/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension ControlProperty { /// Converts `ControlProperty` to `Driver` trait. /// /// `ControlProperty` already can't fail, so no special case needs to be handled. public func asDriver() -> Driver { return self.asDriver { (error) -> Driver in #if DEBUG rxFatalError("Somehow driver received error from a source that shouldn't fail.") #else return Driver.empty() #endif } } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Driver/Driver+Subscription.swift ================================================ // // Driver+Subscription.swift // RxCocoa // // Created by Krunoslav Zaher on 9/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif private let errorMessage = "`drive*` family of methods can be only called from `MainThread`.\n" + "This is required to ensure that the last replayed `Driver` element is delivered on `MainThread`.\n" extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { /** Creates new subscription and sends elements to observer. This method can be only called from `MainThread`. In this form it's equivalent to `subscribe` method, but it communicates intent better. - parameter observer: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public func drive(_ observer: O) -> Disposable where O.E == E { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return self.asSharedSequence().asObservable().subscribe(observer) } /** Creates new subscription and sends elements to observer. This method can be only called from `MainThread`. In this form it's equivalent to `subscribe` method, but it communicates intent better. - parameter observer: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public func drive(_ observer: O) -> Disposable where O.E == E? { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return self.asSharedSequence().asObservable().map { $0 as E? }.subscribe(observer) } /** Creates new subscription and sends elements to `BehaviorRelay`. This method can be only called from `MainThread`. - parameter variable: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer from the variable. */ public func drive(_ relay: BehaviorRelay) -> Disposable { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return drive(onNext: { e in relay.accept(e) }) } /** Creates new subscription and sends elements to variable. This method can be only called from `MainThread`. - parameter variable: Target variable for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer from the variable. */ public func drive(_ relay: BehaviorRelay) -> Disposable { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return drive(onNext: { e in relay.accept(e) }) } /** Subscribes to observable sequence using custom binder function. This method can be only called from `MainThread`. - parameter with: Function used to bind elements from `self`. - returns: Object representing subscription. */ public func drive(_ transformation: (Observable) -> R) -> R { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return transformation(self.asObservable()) } /** Subscribes to observable sequence using custom binder function and final parameter passed to binder function after `self` is passed. public func drive(with: Self -> R1 -> R2, curriedArgument: R1) -> R2 { return with(self)(curriedArgument) } This method can be only called from `MainThread`. - parameter with: Function used to bind elements from `self`. - parameter curriedArgument: Final argument passed to `binder` to finish binding process. - returns: Object representing subscription. */ public func drive(_ with: (Observable) -> (R1) -> R2, curriedArgument: R1) -> R2 { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return with(self.asObservable())(curriedArgument) } /** Subscribes an element handler, a completion handler and disposed handler to an observable sequence. This method can be only called from `MainThread`. Error callback is not exposed because `Driver` can't error out. - parameter onNext: Action to invoke for each element in the observable sequence. - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. gracefully completed, errored, or if the generation is canceled by disposing subscription) - parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has gracefully completed, errored, or if the generation is canceled by disposing subscription) - returns: Subscription object used to unsubscribe from the observable sequence. */ public func drive(onNext: ((E) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { MainScheduler.ensureExecutingOnScheduler(errorMessage: errorMessage) return self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Driver/Driver.swift ================================================ // // Driver.swift // RxCocoa // // Created by Krunoslav Zaher on 9/26/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif /** Trait that represents observable sequence with following properties: - it never fails - it delivers events on `MainScheduler.instance` - `share(replay: 1, scope: .whileConnected)` sharing strategy Additional explanation: - all observers share sequence computation resources - it's stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced - computation of elements is reference counted with respect to the number of observers - if there are no subscribers, it will release sequence computation resources In case trait that models event bus is required, please check `Signal`. `Driver` can be considered a builder pattern for observable sequences that drive the application. If observable sequence has produced at least one element, after new subscription is made last produced element will be immediately replayed on the same thread on which the subscription was made. When using `drive*`, `subscribe*` and `bind*` family of methods, they should always be called from main thread. If `drive*`, `subscribe*` and `bind*` are called from background thread, it is possible that initial replay will happen on background thread, and subsequent events will arrive on main thread. To find out more about traits and how to use them, please visit `Documentation/Traits.md`. */ public typealias Driver = SharedSequence public struct DriverSharingStrategy: SharingStrategyProtocol { public static var scheduler: SchedulerType { return SharingScheduler.make() } public static func share(_ source: Observable) -> Observable { return source.share(replay: 1, scope: .whileConnected) } } extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy { /// Adds `asDriver` to `SharingSequence` with `DriverSharingStrategy`. public func asDriver() -> Driver { return self.asSharedSequence() } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift ================================================ // // ObservableConvertibleType+Driver.swift // RxCocoa // // Created by Krunoslav Zaher on 9/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension ObservableConvertibleType { /** Converts observable sequence to `Driver` trait. - parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence. - returns: Driver trait. */ public func asDriver(onErrorJustReturn: E) -> Driver { let source = self .asObservable() .observeOn(DriverSharingStrategy.scheduler) .catchErrorJustReturn(onErrorJustReturn) return Driver(source) } /** Converts observable sequence to `Driver` trait. - parameter onErrorDriveWith: Driver that continues to drive the sequence in case of error. - returns: Driver trait. */ public func asDriver(onErrorDriveWith: Driver) -> Driver { let source = self .asObservable() .observeOn(DriverSharingStrategy.scheduler) .catchError { _ in onErrorDriveWith.asObservable() } return Driver(source) } /** Converts observable sequence to `Driver` trait. - parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error. - returns: Driver trait. */ public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver) -> Driver { let source = self .asObservable() .observeOn(DriverSharingStrategy.scheduler) .catchError { error in onErrorRecover(error).asObservable() } return Driver(source) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/PublishRelay.swift ================================================ // // PublishRelay.swift // RxCocoa // // Created by Krunoslav Zaher on 3/28/15. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif /// PublishRelay is a wrapper for `PublishSubject`. /// /// Unlike `PublishSubject` it can't terminate with error or completed. public final class PublishRelay: ObservableType { public typealias E = Element private let _subject: PublishSubject // Accepts `event` and emits it to subscribers public func accept(_ event: Element) { _subject.onNext(event) } /// Initializes variable with initial value. public init() { _subject = PublishSubject() } /// Subscribes observer public func subscribe(_ observer: O) -> Disposable where O.E == E { return _subject.subscribe(observer) } /// - returns: Canonical interface for push style sequence public func asObservable() -> Observable { return _subject.asObservable() } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift ================================================ // // SchedulerType+SharedSequence.swift // RxCocoa // // Created by Krunoslav Zaher on 8/27/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // import RxSwift public enum SharingScheduler { /// Default scheduler used in SharedSequence based traits. public private(set) static var make: () -> SchedulerType = { MainScheduler() } /** This method can be used in unit tests to ensure that built in shared sequences are using mock schedulers instead of main schedulers. **This shouldn't be used in normal release builds.** */ static public func mock(scheduler: SchedulerType, action: () -> ()) { return mock(makeScheduler: { scheduler }, action: action) } /** This method can be used in unit tests to ensure that built in shared sequences are using mock schedulers instead of main schedulers. **This shouldn't be used in normal release builds.** */ static public func mock(makeScheduler: @escaping () -> SchedulerType, action: () -> ()) { let originalMake = make make = makeScheduler action() // If you remove this line , compiler buggy optimizations will change behavior of this code _forceCompilerToStopDoingInsaneOptimizationsThatBreakCode(makeScheduler) // Scary, I know make = originalMake } } #if os(Linux) import Glibc #else import func Foundation.arc4random #endif func _forceCompilerToStopDoingInsaneOptimizationsThatBreakCode(_ scheduler: () -> SchedulerType) { let a: Int32 = 1 #if os(Linux) let b = 314 + Int32(Glibc.random() & 1) #else let b = 314 + Int32(arc4random() & 1) #endif if a == b { print(scheduler()) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift ================================================ // This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project // // SharedSequence+Operators+arity.swift // RxCocoa // // Created by Krunoslav Zaher on 10/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif // 2 extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.E, O2.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable() ) return SharedSequence(source) } } extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.E, O2.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable() ) return SharedSequence(source) } } // 3 extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.E, O2.E, O3.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable() ) return SharedSequence(source) } } extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.E, O2.E, O3.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable() ) return SharedSequence(source) } } // 4 extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable() ) return SharedSequence(source) } } extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable() ) return SharedSequence(source) } } // 5 extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable() ) return SharedSequence(source) } } extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable() ) return SharedSequence(source) } } // 6 extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable() ) return SharedSequence(source) } } extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable() ) return SharedSequence(source) } } // 7 extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable() ) return SharedSequence(source) } } extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable() ) return SharedSequence(source) } } // 8 extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy, SharingStrategy == O8.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy, SharingStrategy == O8.SharingStrategy { let source = Observable.zip( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable() ) return SharedSequence(source) } } extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> E) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy, SharingStrategy == O8.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable(), resultSelector: resultSelector ) return SharedSequence(source) } } extension SharedSequenceConvertibleType where E == Any { /** Merges the specified observable sequences into one observable sequence of element tuples whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, SharingStrategy == O4.SharingStrategy, SharingStrategy == O5.SharingStrategy, SharingStrategy == O6.SharingStrategy, SharingStrategy == O7.SharingStrategy, SharingStrategy == O8.SharingStrategy { let source = Observable.combineLatest( source1.asSharedSequence().asObservable(), source2.asSharedSequence().asObservable(), source3.asSharedSequence().asObservable(), source4.asSharedSequence().asObservable(), source5.asSharedSequence().asObservable(), source6.asSharedSequence().asObservable(), source7.asSharedSequence().asObservable(), source8.asSharedSequence().asObservable() ) return SharedSequence(source) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift ================================================ // // SharedSequence+Operators.swift // RxCocoa // // Created by Krunoslav Zaher on 9/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif // MARK: map extension SharedSequenceConvertibleType { /** Projects each element of an observable sequence into a new form. - parameter selector: A transform function to apply to each source element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ public func map(_ selector: @escaping (E) -> R) -> SharedSequence { let source = self .asObservable() .map(selector) return SharedSequence(source) } } // MARK: filter extension SharedSequenceConvertibleType { /** Filters the elements of an observable sequence based on a predicate. - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. */ public func filter(_ predicate: @escaping (E) -> Bool) -> SharedSequence { let source = self .asObservable() .filter(predicate) return SharedSequence(source) } } // MARK: switchLatest extension SharedSequenceConvertibleType where E : SharedSequenceConvertibleType { /** Transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. Each time a new inner observable sequence is received, unsubscribe from the previous inner observable sequence. - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ public func switchLatest() -> SharedSequence { let source: Observable = self .asObservable() .map { $0.asSharedSequence() } .switchLatest() return SharedSequence(source) } } // MARK: flatMapLatest extension SharedSequenceConvertibleType { /** Projects each element of an observable sequence into a new sequence of observable sequences and then transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. It is a combination of `map` + `switchLatest` operator - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ public func flatMapLatest(_ selector: @escaping (E) -> SharedSequence) -> SharedSequence { let source: Observable = self .asObservable() .flatMapLatest(selector) return SharedSequence(source) } } // MARK: flatMapFirst extension SharedSequenceConvertibleType { /** Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. If element is received while there is some projected observable sequence being merged it will simply be ignored. - parameter selector: A transform function to apply to element that was observed while no observable is executing in parallel. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence that was received while no other sequence was being calculated. */ public func flatMapFirst(_ selector: @escaping (E) -> SharedSequence) -> SharedSequence { let source: Observable = self .asObservable() .flatMapFirst(selector) return SharedSequence(source) } } // MARK: do extension SharedSequenceConvertibleType { /** Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. - parameter onNext: Action to invoke for each element in the observable sequence. - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - parameter onSubscribe: Action to invoke before subscribing to source observable sequence. - parameter onSubscribed: Action to invoke after subscribing to source observable sequence. - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed. - returns: The source sequence with the side-effecting behavior applied. */ public func `do`(onNext: ((E) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onSubscribe: (() -> ())? = nil, onSubscribed: (() -> ())? = nil, onDispose: (() -> ())? = nil) -> SharedSequence { let source = self.asObservable() .do(onNext: onNext, onCompleted: onCompleted, onSubscribe: onSubscribe, onSubscribed: onSubscribed, onDispose: onDispose) return SharedSequence(source) } } // MARK: debug extension SharedSequenceConvertibleType { /** Prints received events for all observers on standard output. - parameter identifier: Identifier that is printed together with event description to standard output. - returns: An observable sequence whose events are printed to standard output. */ public func debug(_ identifier: String? = nil, trimOutput: Bool = false, file: String = #file, line: UInt = #line, function: String = #function) -> SharedSequence { let source = self.asObservable() .debug(identifier, trimOutput: trimOutput, file: file, line: line, function: function) return SharedSequence(source) } } // MARK: distinctUntilChanged extension SharedSequenceConvertibleType where E: Equatable { /** Returns an observable sequence that contains only distinct contiguous elements according to equality operator. - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence. */ public func distinctUntilChanged() -> SharedSequence { let source = self.asObservable() .distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) return SharedSequence(source) } } extension SharedSequenceConvertibleType { /** Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`. - parameter keySelector: A function to compute the comparison key for each element. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. */ public func distinctUntilChanged(_ keySelector: @escaping (E) -> K) -> SharedSequence { let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: { $0 == $1 }) return SharedSequence(source) } /** Returns an observable sequence that contains only distinct contiguous elements according to the `comparer`. - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. */ public func distinctUntilChanged(_ comparer: @escaping (E, E) -> Bool) -> SharedSequence { let source = self.asObservable() .distinctUntilChanged({ $0 }, comparer: comparer) return SharedSequence(source) } /** Returns an observable sequence that contains only distinct contiguous elements according to the keySelector and the comparer. - parameter keySelector: A function to compute the comparison key for each element. - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. */ public func distinctUntilChanged(_ keySelector: @escaping (E) -> K, comparer: @escaping (K, K) -> Bool) -> SharedSequence { let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: comparer) return SharedSequence(source) } } // MARK: flatMap extension SharedSequenceConvertibleType { /** Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ public func flatMap(_ selector: @escaping (E) -> SharedSequence) -> SharedSequence { let source = self.asObservable() .flatMap(selector) return SharedSequence(source) } } // MARK: merge extension SharedSequenceConvertibleType { /** Merges elements from all observable sequences from collection into a single observable sequence. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - parameter sources: Collection of observable sequences to merge. - returns: The observable sequence that merges the elements of the observable sequences. */ public static func merge(_ sources: C) -> SharedSequence where C.Iterator.Element == SharedSequence { let source = Observable.merge(sources.map { $0.asObservable() }) return SharedSequence(source) } /** Merges elements from all observable sequences from array into a single observable sequence. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - parameter sources: Array of observable sequences to merge. - returns: The observable sequence that merges the elements of the observable sequences. */ public static func merge(_ sources: [SharedSequence]) -> SharedSequence { let source = Observable.merge(sources.map { $0.asObservable() }) return SharedSequence(source) } /** Merges elements from all observable sequences into a single observable sequence. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - parameter sources: Collection of observable sequences to merge. - returns: The observable sequence that merges the elements of the observable sequences. */ public static func merge(_ sources: SharedSequence...) -> SharedSequence { let source = Observable.merge(sources.map { $0.asObservable() }) return SharedSequence(source) } } // MARK: merge extension SharedSequenceConvertibleType where E : SharedSequenceConvertibleType { /** Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. - returns: The observable sequence that merges the elements of the observable sequences. */ public func merge() -> SharedSequence { let source = self.asObservable() .map { $0.asSharedSequence() } .merge() return SharedSequence(source) } /** Merges elements from all inner observable sequences into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences. - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently. - returns: The observable sequence that merges the elements of the inner sequences. */ public func merge(maxConcurrent: Int) -> SharedSequence { let source = self.asObservable() .map { $0.asSharedSequence() } .merge(maxConcurrent: maxConcurrent) return SharedSequence(source) } } // MARK: throttle extension SharedSequenceConvertibleType { /** Returns an Observable that emits the first and the latest item emitted by the source Observable during sequential time windows of a specified duration. This operator makes sure that no two elements are emitted in less then dueTime. - seealso: [debounce operator on reactivex.io](http://reactivex.io/documentation/operators/debounce.html) - parameter dueTime: Throttling duration for each element. - parameter latest: Should latest element received in a dueTime wide time window since last element emission be emitted. - returns: The throttled sequence. */ public func throttle(_ dueTime: RxTimeInterval, latest: Bool = true) -> SharedSequence { let source = self.asObservable() .throttle(dueTime, latest: latest, scheduler: SharingStrategy.scheduler) return SharedSequence(source) } /** Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers. - parameter dueTime: Throttling duration for each element. - returns: The throttled sequence. */ public func debounce(_ dueTime: RxTimeInterval) -> SharedSequence { let source = self.asObservable() .debounce(dueTime, scheduler: SharingStrategy.scheduler) return SharedSequence(source) } } // MARK: scan extension SharedSequenceConvertibleType { /** Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value. For aggregation behavior with no intermediate results, see `reduce`. - parameter seed: The initial accumulator value. - parameter accumulator: An accumulator function to be invoked on each element. - returns: An observable sequence containing the accumulated values. */ public func scan(_ seed: A, accumulator: @escaping (A, E) -> A) -> SharedSequence { let source = self.asObservable() .scan(seed, accumulator: accumulator) return SharedSequence(source) } } // MARK: concat extension SharedSequence { /** Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully. - returns: An observable sequence that contains the elements of each given sequence, in sequential order. */ public static func concat(_ sequence: S) -> SharedSequence where S.Iterator.Element == SharedSequence { let source = Observable.concat(sequence.lazy.map { $0.asObservable() }) return SharedSequence(source) } /** Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully. - returns: An observable sequence that contains the elements of each given sequence, in sequential order. */ public static func concat(_ collection: C) -> SharedSequence where C.Iterator.Element == SharedSequence { let source = Observable.concat(collection.map { $0.asObservable() }) return SharedSequence(source) } } // MARK: zip extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip(_ collection: C, _ resultSelector: @escaping ([Element]) throws -> R) -> SharedSequence where C.Iterator.Element == SharedSequence { let source = Observable.zip(collection.map { $0.asSharedSequence().asObservable() }, resultSelector) return SharedSequence(source) } /** Merges the specified observable sequences into one observable sequence all of the observable sequences have produced an element at a corresponding index. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip(_ collection: C) -> SharedSequence where C.Iterator.Element == SharedSequence { let source = Observable.zip(collection.map { $0.asSharedSequence().asObservable() }) return SharedSequence(source) } } // MARK: combineLatest extension SharedSequence { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest(_ collection: C, _ resultSelector: @escaping ([Element]) throws -> R) -> SharedSequence where C.Iterator.Element == SharedSequence { let source = Observable.combineLatest(collection.map { $0.asObservable() }, resultSelector) return SharedSequence(source) } /** Merges the specified observable sequences into one observable sequence whenever any of the observable sequences produces an element. - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest(_ collection: C) -> SharedSequence where C.Iterator.Element == SharedSequence { let source = Observable.combineLatest(collection.map { $0.asObservable() }) return SharedSequence(source) } } // MARK: withLatestFrom extension SharedSequenceConvertibleType { /** Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any. - parameter second: Second observable source. - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any. - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(_ second: SecondO, resultSelector: @escaping (E, SecondO.E) -> ResultType) -> SharedSequence where SecondO.SharingStrategy == SharingStrategy { let source = self.asObservable() .withLatestFrom(second.asSharedSequence(), resultSelector: resultSelector) return SharedSequence(source) } /** Merges two observable sequences into one observable sequence by using latest element from the second sequence every time when `self` emits an element. - parameter second: Second observable source. - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(_ second: SecondO) -> SharedSequence { let source = self.asObservable() .withLatestFrom(second.asSharedSequence()) return SharedSequence(source) } } // MARK: skip extension SharedSequenceConvertibleType { /** Bypasses a specified number of elements in an observable sequence and then returns the remaining elements. - seealso: [skip operator on reactivex.io](http://reactivex.io/documentation/operators/skip.html) - parameter count: The number of elements to skip before returning the remaining elements. - returns: An observable sequence that contains the elements that occur after the specified index in the input sequence. */ public func skip(_ count: Int) -> SharedSequence { let source = self.asObservable() .skip(count) return SharedSequence(source) } } // MARK: startWith extension SharedSequenceConvertibleType { /** Prepends a value to an observable sequence. - seealso: [startWith operator on reactivex.io](http://reactivex.io/documentation/operators/startwith.html) - parameter element: Element to prepend to the specified sequence. - returns: The source sequence prepended with the specified values. */ public func startWith(_ element: E) -> SharedSequence { let source = self.asObservable() .startWith(element) return SharedSequence(source) } } // MARK: delay extension SharedSequenceConvertibleType { /** Returns an observable sequence by the source observable sequence shifted forward in time by a specified delay. Error events from the source observable sequence are not delayed. - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html) - parameter dueTime: Relative time shift of the source by. - parameter scheduler: Scheduler to run the subscription delay timer on. - returns: the source Observable shifted in time by the specified delay. */ public func delay(_ dueTime: RxTimeInterval) -> SharedSequence { let source = self.asObservable() .delay(dueTime, scheduler: SharingStrategy.scheduler) return SharedSequence(source) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence.swift ================================================ // // SharedSequence.swift // RxCocoa // // Created by Krunoslav Zaher on 8/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif /** Trait that represents observable sequence that shares computation resources with following properties: - it never fails - it delivers events on `SharingStrategy.scheduler` - sharing strategy is customizable using `SharingStrategy.share` behavior `SharedSequence` can be considered a builder pattern for observable sequences that share computation resources. To find out more about units and how to use them, please visit `Documentation/Traits.md`. */ public struct SharedSequence : SharedSequenceConvertibleType { public typealias E = Element public typealias SharingStrategy = S let _source: Observable init(_ source: Observable) { self._source = S.share(source) } init(raw: Observable) { self._source = raw } #if EXPANDABLE_SHARED_SEQUENCE /** This method is extension hook in case this unit needs to extended from outside the library. By defining `EXPANDABLE_SHARED_SEQUENCE` one agrees that it's up to him to ensure shared sequence properties are preserved after extension. */ public static func createUnsafe(source: O) -> SharedSequence { return SharedSequence(raw: source.asObservable()) } #endif /** - returns: Built observable sequence. */ public func asObservable() -> Observable { return _source } /** - returns: `self` */ public func asSharedSequence() -> SharedSequence { return self } } /** Different `SharedSequence` sharing strategies must conform to this protocol. */ public protocol SharingStrategyProtocol { /** Scheduled on which all sequence events will be delivered. */ static var scheduler: SchedulerType { get } /** Computation resources sharing strategy for multiple sequence observers. E.g. One can choose `share(replay:scope:)` as sequence event sharing strategies, but also do something more exotic, like implementing promises or lazy loading chains. */ static func share(_ source: Observable) -> Observable } /** A type that can be converted to `SharedSequence`. */ public protocol SharedSequenceConvertibleType : ObservableConvertibleType { associatedtype SharingStrategy: SharingStrategyProtocol /** Converts self to `SharedSequence`. */ func asSharedSequence() -> SharedSequence } extension SharedSequenceConvertibleType { public func asObservable() -> Observable { return asSharedSequence().asObservable() } } extension SharedSequence { /** Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message. - returns: An observable sequence with no elements. */ public static func empty() -> SharedSequence { return SharedSequence(raw: Observable.empty().subscribeOn(S.scheduler)) } /** Returns a non-terminating observable sequence, which can be used to denote an infinite duration. - returns: An observable sequence whose observers will never get called. */ public static func never() -> SharedSequence { return SharedSequence(raw: Observable.never()) } /** Returns an observable sequence that contains a single element. - parameter element: Single element in the resulting observable sequence. - returns: An observable sequence containing the single specified element. */ public static func just(_ element: E) -> SharedSequence { return SharedSequence(raw: Observable.just(element).subscribeOn(S.scheduler)) } /** Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. - parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence. - returns: An observable sequence whose observers trigger an invocation of the given observable factory function. */ public static func deferred(_ observableFactory: @escaping () -> SharedSequence) -> SharedSequence { return SharedSequence(Observable.deferred { observableFactory().asObservable() }) } /** This method creates a new Observable instance with a variable number of elements. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - parameter elements: Elements to generate. - returns: The observable sequence whose elements are pulled from the given arguments. */ public static func of(_ elements: E ...) -> SharedSequence { let source = Observable.from(elements, scheduler: S.scheduler) return SharedSequence(raw: source) } } extension SharedSequence { /** This method converts an array to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - returns: The observable sequence whose elements are pulled from the given enumerable sequence. */ public static func from(_ array: [E]) -> SharedSequence { let source = Observable.from(array, scheduler: S.scheduler) return SharedSequence(raw: source) } /** This method converts a sequence to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - returns: The observable sequence whose elements are pulled from the given enumerable sequence. */ public static func from(_ sequence: S) -> SharedSequence where S.Iterator.Element == E { let source = Observable.from(sequence, scheduler: SharingStrategy.scheduler) return SharedSequence(raw: source) } /** This method converts a optional to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - parameter optional: Optional element in the resulting observable sequence. - returns: An observable sequence containing the wrapped value or not from given optional. */ public static func from(optional: E?) -> SharedSequence { let source = Observable.from(optional: optional, scheduler: S.scheduler) return SharedSequence(raw: source) } } extension SharedSequence where Element : RxAbstractInteger { /** Returns an observable sequence that produces a value after each period, using the specified scheduler to run timers and to send out observer messages. - seealso: [interval operator on reactivex.io](http://reactivex.io/documentation/operators/interval.html) - parameter period: Period for producing the values in the resulting sequence. - returns: An observable sequence that produces a value after each period. */ public static func interval(_ period: RxTimeInterval) -> SharedSequence { return SharedSequence(Observable.interval(period, scheduler: S.scheduler)) } } // MARK: timer extension SharedSequence where Element: RxAbstractInteger { /** Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers. - seealso: [timer operator on reactivex.io](http://reactivex.io/documentation/operators/timer.html) - parameter dueTime: Relative time at which to produce the first value. - parameter period: Period to produce subsequent values. - returns: An observable sequence that produces a value after due time has elapsed and then each period. */ public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval) -> SharedSequence { return SharedSequence(Observable.timer(dueTime, period: period, scheduler: S.scheduler)) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift ================================================ // // ObservableConvertibleType+Signal.swift // RxCocoa // // Created by Krunoslav Zaher on 9/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension ObservableConvertibleType { /** Converts observable sequence to `Signal` trait. - parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence. - returns: Signal trait. */ public func asSignal(onErrorJustReturn: E) -> Signal { let source = self .asObservable() .observeOn(SignalSharingStrategy.scheduler) .catchErrorJustReturn(onErrorJustReturn) return Signal(source) } /** Converts observable sequence to `Driver` trait. - parameter onErrorDriveWith: Driver that continues to drive the sequence in case of error. - returns: Signal trait. */ public func asSignal(onErrorSignalWith: Signal) -> Signal { let source = self .asObservable() .observeOn(SignalSharingStrategy.scheduler) .catchError { _ in onErrorSignalWith.asObservable() } return Signal(source) } /** Converts observable sequence to `Driver` trait. - parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error. - returns: Signal trait. */ public func asSignal(onErrorRecover: @escaping (_ error: Swift.Error) -> Signal) -> Signal { let source = self .asObservable() .observeOn(SignalSharingStrategy.scheduler) .catchError { error in onErrorRecover(error).asObservable() } return Signal(source) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Signal/PublishRelay+Signal.swift ================================================ // // PublishRelay+Signal.swift // RxCocoa // // Created by Krunoslav Zaher on 12/28/15. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension PublishRelay { /// Converts `PublishRelay` to `Signal`. /// /// - returns: Observable sequence. public func asSignal() -> Signal { let source = self.asObservable() .observeOn(SignalSharingStrategy.scheduler) return SharedSequence(source) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Signal/Signal+Subscription.swift ================================================ // // Signal+Subscription.swift // RxCocoa // // Created by Krunoslav Zaher on 9/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingStrategy { /** Creates new subscription and sends elements to observer. In this form it's equivalent to `subscribe` method, but it communicates intent better. - parameter to: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public func emit(to observer: O) -> Disposable where O.E == E { return self.asSharedSequence().asObservable().subscribe(observer) } /** Creates new subscription and sends elements to observer. In this form it's equivalent to `subscribe` method, but it communicates intent better. - parameter to: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public func emit(to observer: O) -> Disposable where O.E == E? { return self.asSharedSequence().asObservable().map { $0 as E? }.subscribe(observer) } /** Creates new subscription and sends elements to variable. - parameter relay: Target relay for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer from the variable. */ public func emit(to relay: PublishRelay) -> Disposable { return emit(onNext: { e in relay.accept(e) }) } /** Creates new subscription and sends elements to variable. - parameter to: Target relay for sequence elements. - returns: Disposable object that can be used to unsubscribe the observer from the variable. */ public func emit(to relay: PublishRelay) -> Disposable { return emit(onNext: { e in relay.accept(e) }) } /** Subscribes an element handler, a completion handler and disposed handler to an observable sequence. Error callback is not exposed because `Signal` can't error out. - parameter onNext: Action to invoke for each element in the observable sequence. - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. gracefully completed, errored, or if the generation is canceled by disposing subscription) - parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has gracefully completed, errored, or if the generation is canceled by disposing subscription) - returns: Subscription object used to unsubscribe from the observable sequence. */ public func emit(onNext: ((E) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { return self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed) } } ================================================ FILE: Pods/RxCocoa/RxCocoa/Traits/Signal/Signal.swift ================================================ // // Signal.swift // RxCocoa // // Created by Krunoslav Zaher on 9/26/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if !RX_NO_MODULE import RxSwift #endif /** Trait that represents observable sequence with following properties: - it never fails - it delivers events on `MainScheduler.instance` - `share(scope: .whileConnected)` sharing strategy Additional explanation: - all observers share sequence computation resources - there is no replaying of sequence elements on new observer subscription - computation of elements is reference counted with respect to the number of observers - if there are no subscribers, it will release sequence computation resources In case trait that models state propagation is required, please check `Driver`. `Signal` can be considered a builder pattern for observable sequences that model imperative events part of the application. To find out more about units and how to use them, please visit `Documentation/Traits.md`. */ public typealias Signal = SharedSequence public struct SignalSharingStrategy : SharingStrategyProtocol { public static var scheduler: SchedulerType { return SharingScheduler.make() } public static func share(_ source: Observable) -> Observable { return source.share(scope: .whileConnected) } } extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingStrategy { /// Adds `asPublisher` to `SharingSequence` with `PublishSharingStrategy`. public func asSignal() -> Signal { return asSharedSequence() } } ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift ================================================ // // RxCollectionViewReactiveArrayDataSource.swift // RxCocoa // // Created by Krunoslav Zaher on 6/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif // objc monkey business class _RxCollectionViewReactiveArrayDataSource : NSObject , UICollectionViewDataSource { @objc(numberOfSectionsInCollectionView:) func numberOfSections(in: UICollectionView) -> Int { return 1 } func _collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 0 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return _collectionView(collectionView, numberOfItemsInSection: section) } fileprivate func _collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { rxAbstractMethod() } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return _collectionView(collectionView, cellForItemAt: indexPath) } } class RxCollectionViewReactiveArrayDataSourceSequenceWrapper : RxCollectionViewReactiveArrayDataSource , RxCollectionViewDataSourceType { typealias Element = S override init(cellFactory: @escaping CellFactory) { super.init(cellFactory: cellFactory) } func collectionView(_ collectionView: UICollectionView, observedEvent: Event) { Binder(self) { collectionViewDataSource, sectionModels in let sections = Array(sectionModels) collectionViewDataSource.collectionView(collectionView, observedElements: sections) }.on(observedEvent) } } // Please take a look at `DelegateProxyType.swift` class RxCollectionViewReactiveArrayDataSource : _RxCollectionViewReactiveArrayDataSource , SectionedViewDataSourceType { typealias CellFactory = (UICollectionView, Int, Element) -> UICollectionViewCell var itemModels: [Element]? = nil func modelAtIndex(_ index: Int) -> Element? { return itemModels?[index] } func model(at indexPath: IndexPath) throws -> Any { precondition(indexPath.section == 0) guard let item = itemModels?[indexPath.item] else { throw RxCocoaError.itemsNotYetBound(object: self) } return item } var cellFactory: CellFactory init(cellFactory: @escaping CellFactory) { self.cellFactory = cellFactory } // data source override func _collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return itemModels?.count ?? 0 } override func _collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return cellFactory(collectionView, indexPath.item, itemModels![indexPath.item]) } // reactive func collectionView(_ collectionView: UICollectionView, observedElements: [Element]) { self.itemModels = observedElements collectionView.reloadData() // workaround for http://stackoverflow.com/questions/39867325/ios-10-bug-uicollectionview-received-layout-attributes-for-a-cell-with-an-index collectionView.collectionViewLayout.invalidateLayout() } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift ================================================ // // RxPickerViewAdapter.swift // RxCocoa // // Created by Sergey Shulga on 12/07/2017. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif class RxPickerViewArrayDataSource: NSObject, UIPickerViewDataSource, SectionedViewDataSourceType { fileprivate var items: [T] = [] func model(at indexPath: IndexPath) throws -> Any { guard items.indices ~= indexPath.row else { throw RxCocoaError.itemsNotYetBound(object: self) } return items[indexPath.row] } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return items.count } } class RxPickerViewSequenceDataSource : RxPickerViewArrayDataSource , RxPickerViewDataSourceType { typealias Element = S func pickerView(_ pickerView: UIPickerView, observedEvent: Event) { Binder(self) { dataSource, items in dataSource.items = items pickerView.reloadAllComponents() } .on(observedEvent.map(Array.init)) } } final class RxStringPickerViewAdapter : RxPickerViewSequenceDataSource , UIPickerViewDelegate { typealias TitleForRow = (Int, S.Iterator.Element) -> String? private let titleForRow: TitleForRow init(titleForRow: @escaping TitleForRow) { self.titleForRow = titleForRow super.init() } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return titleForRow(row, items[row]) } } final class RxAttributedStringPickerViewAdapter: RxPickerViewSequenceDataSource, UIPickerViewDelegate { typealias AttributedTitleForRow = (Int, S.Iterator.Element) -> NSAttributedString? private let attributedTitleForRow: AttributedTitleForRow init(attributedTitleForRow: @escaping AttributedTitleForRow) { self.attributedTitleForRow = attributedTitleForRow super.init() } func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? { return attributedTitleForRow(row, items[row]) } } final class RxPickerViewAdapter: RxPickerViewSequenceDataSource, UIPickerViewDelegate { typealias ViewForRow = (Int, S.Iterator.Element, UIView?) -> UIView private let viewForRow: ViewForRow init(viewForRow: @escaping ViewForRow) { self.viewForRow = viewForRow super.init() } func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { return viewForRow(row, items[row], view) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift ================================================ // // RxTableViewReactiveArrayDataSource.swift // RxCocoa // // Created by Krunoslav Zaher on 6/26/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif // objc monkey business class _RxTableViewReactiveArrayDataSource : NSObject , UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 1 } func _tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 0 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return _tableView(tableView, numberOfRowsInSection: section) } fileprivate func _tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { rxAbstractMethod() } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return _tableView(tableView, cellForRowAt: indexPath) } } class RxTableViewReactiveArrayDataSourceSequenceWrapper : RxTableViewReactiveArrayDataSource , RxTableViewDataSourceType { typealias Element = S override init(cellFactory: @escaping CellFactory) { super.init(cellFactory: cellFactory) } func tableView(_ tableView: UITableView, observedEvent: Event) { Binder(self) { tableViewDataSource, sectionModels in let sections = Array(sectionModels) tableViewDataSource.tableView(tableView, observedElements: sections) }.on(observedEvent) } } // Please take a look at `DelegateProxyType.swift` class RxTableViewReactiveArrayDataSource : _RxTableViewReactiveArrayDataSource , SectionedViewDataSourceType { typealias CellFactory = (UITableView, Int, Element) -> UITableViewCell var itemModels: [Element]? = nil func modelAtIndex(_ index: Int) -> Element? { return itemModels?[index] } func model(at indexPath: IndexPath) throws -> Any { precondition(indexPath.section == 0) guard let item = itemModels?[indexPath.item] else { throw RxCocoaError.itemsNotYetBound(object: self) } return item } let cellFactory: CellFactory init(cellFactory: @escaping CellFactory) { self.cellFactory = cellFactory } override func _tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return itemModels?.count ?? 0 } override func _tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return cellFactory(tableView, indexPath.item, itemModels![indexPath.row]) } // reactive func tableView(_ tableView: UITableView, observedElements: [Element]) { self.itemModels = observedElements tableView.reloadData() } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Events/ItemEvents.swift ================================================ // // ItemEvents.swift // RxCocoa // // Created by Krunoslav Zaher on 6/20/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit public typealias ItemMovedEvent = (sourceIndex: IndexPath, destinationIndex: IndexPath) public typealias WillDisplayCellEvent = (cell: UITableViewCell, indexPath: IndexPath) public typealias DidEndDisplayingCellEvent = (cell: UITableViewCell, indexPath: IndexPath) #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/NSTextStorage+Rx.swift ================================================ // // NSTextStorage+Rx.swift // RxCocoa // // Created by Segii Shulga on 12/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: NSTextStorage { /// Reactive wrapper for `delegate`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxTextStorageDelegateProxy.proxy(for: base) } /// Reactive wrapper for `delegate` message. public var didProcessEditingRangeChangeInLength: Observable<(editedMask:NSTextStorageEditActions, editedRange:NSRange, delta:Int)> { return delegate .methodInvoked(#selector(NSTextStorageDelegate.textStorage(_:didProcessEditing:range:changeInLength:))) .map { a in let editedMask = NSTextStorageEditActions(rawValue: try castOrThrow(UInt.self, a[1]) ) let editedRange = try castOrThrow(NSValue.self, a[2]).rangeValue let delta = try castOrThrow(Int.self, a[3]) return (editedMask, editedRange, delta) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Protocols/RxCollectionViewDataSourceType.swift ================================================ // // RxCollectionViewDataSourceType.swift // RxCocoa // // Created by Krunoslav Zaher on 6/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /// Marks data source as `UICollectionView` reactive data source enabling it to be used with one of the `bindTo` methods. public protocol RxCollectionViewDataSourceType /*: UICollectionViewDataSource*/ { /// Type of elements that can be bound to collection view. associatedtype Element /// New observable sequence event observed. /// /// - parameter collectionView: Bound collection view. /// - parameter observedEvent: Event func collectionView(_ collectionView: UICollectionView, observedEvent: Event) -> Void } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Protocols/RxPickerViewDataSourceType.swift ================================================ // // RxPickerViewDataSourceType.swift // RxCocoa // // Created by Sergey Shulga on 05/07/2017. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /// Marks data source as `UIPickerView` reactive data source enabling it to be used with one of the `bindTo` methods. public protocol RxPickerViewDataSourceType { /// Type of elements that can be bound to picker view. associatedtype Element /// New observable sequence event observed. /// /// - parameter pickerView: Bound picker view. /// - parameter observedEvent: Event func pickerView(_ pickerView: UIPickerView, observedEvent: Event) } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Protocols/RxTableViewDataSourceType.swift ================================================ // // RxTableViewDataSourceType.swift // RxCocoa // // Created by Krunoslav Zaher on 6/26/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /// Marks data source as `UITableView` reactive data source enabling it to be used with one of the `bindTo` methods. public protocol RxTableViewDataSourceType /*: UITableViewDataSource*/ { /// Type of elements that can be bound to table view. associatedtype Element /// New observable sequence event observed. /// /// - parameter tableView: Bound table view. /// - parameter observedEvent: Event func tableView(_ tableView: UITableView, observedEvent: Event) -> Void } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDataSourceProxy.swift ================================================ // // RxCollectionViewDataSourceProxy.swift // RxCocoa // // Created by Krunoslav Zaher on 6/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UICollectionView: HasDataSource { public typealias DataSource = UICollectionViewDataSource } let collectionViewDataSourceNotSet = CollectionViewDataSourceNotSet() final class CollectionViewDataSourceNotSet : NSObject , UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 0 } // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath: func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { rxAbstractMethod(message: dataSourceNotSet) } } /// For more information take a look at `DelegateProxyType`. open class RxCollectionViewDataSourceProxy : DelegateProxy , DelegateProxyType , UICollectionViewDataSource { /// Typed parent object. public weak private(set) var collectionView: UICollectionView? /// - parameter collectionView: Parent object for delegate proxy. public init(collectionView: ParentObject) { self.collectionView = collectionView super.init(parentObject: collectionView, delegateProxy: RxCollectionViewDataSourceProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxCollectionViewDataSourceProxy(collectionView: $0) } } private weak var _requiredMethodsDataSource: UICollectionViewDataSource? = collectionViewDataSourceNotSet // MARK: delegate /// Required delegate method implementation. public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return (_requiredMethodsDataSource ?? collectionViewDataSourceNotSet).collectionView(collectionView, numberOfItemsInSection: section) } /// Required delegate method implementation. public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return (_requiredMethodsDataSource ?? collectionViewDataSourceNotSet).collectionView(collectionView, cellForItemAt: indexPath) } /// For more information take a look at `DelegateProxyType`. open override func setForwardToDelegate(_ forwardToDelegate: UICollectionViewDataSource?, retainDelegate: Bool) { _requiredMethodsDataSource = forwardToDelegate ?? collectionViewDataSourceNotSet super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift ================================================ // // RxCollectionViewDelegateProxy.swift // RxCocoa // // Created by Krunoslav Zaher on 6/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /// For more information take a look at `DelegateProxyType`. open class RxCollectionViewDelegateProxy : RxScrollViewDelegateProxy , UICollectionViewDelegate , UICollectionViewDelegateFlowLayout { /// Typed parent object. public weak private(set) var collectionView: UICollectionView? /// Initializes `RxCollectionViewDelegateProxy` /// /// - parameter collectionView: Parent object for delegate proxy. public init(collectionView: UICollectionView) { self.collectionView = collectionView super.init(scrollView: collectionView) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxNavigationControllerDelegateProxy.swift ================================================ // // RxNavigationControllerDelegateProxy.swift // RxCocoa // // Created by Diogo on 13/04/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UINavigationController: HasDelegate { public typealias Delegate = UINavigationControllerDelegate } /// For more information take a look at `DelegateProxyType`. open class RxNavigationControllerDelegateProxy : DelegateProxy , DelegateProxyType , UINavigationControllerDelegate { /// Typed parent object. public weak private(set) var navigationController: UINavigationController? /// - parameter navigationController: Parent object for delegate proxy. public init(navigationController: ParentObject) { self.navigationController = navigationController super.init(parentObject: navigationController, delegateProxy: RxNavigationControllerDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxNavigationControllerDelegateProxy(navigationController: $0) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxPickerViewDataSourceProxy.swift ================================================ // // RxPickerViewDataSourceProxy.swift // RxCocoa // // Created by Sergey Shulga on 05/07/2017. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UIPickerView: HasDataSource { public typealias DataSource = UIPickerViewDataSource } fileprivate let pickerViewDataSourceNotSet = PickerViewDataSourceNotSet() final fileprivate class PickerViewDataSourceNotSet: NSObject, UIPickerViewDataSource { func numberOfComponents(in pickerView: UIPickerView) -> Int { return 0 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return 0 } } /// For more information take a look at `DelegateProxyType`. public class RxPickerViewDataSourceProxy : DelegateProxy , DelegateProxyType , UIPickerViewDataSource { /// Typed parent object. public weak private(set) var pickerView: UIPickerView? /// - parameter pickerView: Parent object for delegate proxy. public init(pickerView: ParentObject) { self.pickerView = pickerView super.init(parentObject: pickerView, delegateProxy: RxPickerViewDataSourceProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxPickerViewDataSourceProxy(pickerView: $0) } } private weak var _requiredMethodsDataSource: UIPickerViewDataSource? = pickerViewDataSourceNotSet // MARK: UIPickerViewDataSource /// Required delegate method implementation. public func numberOfComponents(in pickerView: UIPickerView) -> Int { return (_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).numberOfComponents(in: pickerView) } /// Required delegate method implementation. public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return (_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).pickerView(pickerView, numberOfRowsInComponent: component) } /// For more information take a look at `DelegateProxyType`. public override func setForwardToDelegate(_ forwardToDelegate: UIPickerViewDataSource?, retainDelegate: Bool) { _requiredMethodsDataSource = forwardToDelegate ?? pickerViewDataSourceNotSet super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxPickerViewDelegateProxy.swift ================================================ // // RxPickerViewDelegateProxy.swift // RxCocoa // // Created by Segii Shulga on 5/12/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension UIPickerView: HasDelegate { public typealias Delegate = UIPickerViewDelegate } open class RxPickerViewDelegateProxy : DelegateProxy , DelegateProxyType , UIPickerViewDelegate { /// Typed parent object. public weak private(set) var pickerView: UIPickerView? /// - parameter pickerView: Parent object for delegate proxy. public init(pickerView: ParentObject) { self.pickerView = pickerView super.init(parentObject: pickerView, delegateProxy: RxPickerViewDelegateProxy.self) } // Register known implementationss public static func registerKnownImplementations() { self.register { RxPickerViewDelegateProxy(pickerView: $0) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift ================================================ // // RxScrollViewDelegateProxy.swift // RxCocoa // // Created by Krunoslav Zaher on 6/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension UIScrollView: HasDelegate { public typealias Delegate = UIScrollViewDelegate } /// For more information take a look at `DelegateProxyType`. open class RxScrollViewDelegateProxy : DelegateProxy , DelegateProxyType , UIScrollViewDelegate { /// Typed parent object. public weak private(set) var scrollView: UIScrollView? /// - parameter scrollView: Parent object for delegate proxy. public init(scrollView: ParentObject) { self.scrollView = scrollView super.init(parentObject: scrollView, delegateProxy: RxScrollViewDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxScrollViewDelegateProxy(scrollView: $0) } self.register { RxTableViewDelegateProxy(tableView: $0) } self.register { RxCollectionViewDelegateProxy(collectionView: $0) } self.register { RxTextViewDelegateProxy(textView: $0) } } fileprivate var _contentOffsetBehaviorSubject: BehaviorSubject? fileprivate var _contentOffsetPublishSubject: PublishSubject<()>? /// Optimized version used for observing content offset changes. internal var contentOffsetBehaviorSubject: BehaviorSubject { if let subject = _contentOffsetBehaviorSubject { return subject } let subject = BehaviorSubject(value: self.scrollView?.contentOffset ?? CGPoint.zero) _contentOffsetBehaviorSubject = subject return subject } /// Optimized version used for observing content offset changes. internal var contentOffsetPublishSubject: PublishSubject<()> { if let subject = _contentOffsetPublishSubject { return subject } let subject = PublishSubject<()>() _contentOffsetPublishSubject = subject return subject } // MARK: delegate methods /// For more information take a look at `DelegateProxyType`. public func scrollViewDidScroll(_ scrollView: UIScrollView) { if let subject = _contentOffsetBehaviorSubject { subject.on(.next(scrollView.contentOffset)) } if let subject = _contentOffsetPublishSubject { subject.on(.next(())) } self._forwardToDelegate?.scrollViewDidScroll?(scrollView) } deinit { if let subject = _contentOffsetBehaviorSubject { subject.on(.completed) } if let subject = _contentOffsetPublishSubject { subject.on(.completed) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift ================================================ // // RxSearchBarDelegateProxy.swift // RxCocoa // // Created by Krunoslav Zaher on 7/4/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UISearchBar: HasDelegate { public typealias Delegate = UISearchBarDelegate } /// For more information take a look at `DelegateProxyType`. open class RxSearchBarDelegateProxy : DelegateProxy , DelegateProxyType , UISearchBarDelegate { /// Typed parent object. public weak private(set) var searchBar: UISearchBar? /// - parameter searchBar: Parent object for delegate proxy. public init(searchBar: ParentObject) { self.searchBar = searchBar super.init(parentObject: searchBar, delegateProxy: RxSearchBarDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxSearchBarDelegateProxy(searchBar: $0) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxSearchControllerDelegateProxy.swift ================================================ // // RxSearchControllerDelegateProxy.swift // RxCocoa // // Created by Segii Shulga on 3/17/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension UISearchController: HasDelegate { public typealias Delegate = UISearchControllerDelegate } /// For more information take a look at `DelegateProxyType`. @available(iOS 8.0, *) open class RxSearchControllerDelegateProxy : DelegateProxy , DelegateProxyType , UISearchControllerDelegate { /// Typed parent object. public weak private(set) var searchController: UISearchController? /// - parameter searchController: Parent object for delegate proxy. public init(searchController: UISearchController) { self.searchController = searchController super.init(parentObject: searchController, delegateProxy: RxSearchControllerDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxSearchControllerDelegateProxy(searchController: $0) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTabBarControllerDelegateProxy.swift ================================================ // // RxTabBarControllerDelegateProxy.swift // RxCocoa // // Created by Yusuke Kita on 2016/12/07. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UITabBarController: HasDelegate { public typealias Delegate = UITabBarControllerDelegate } /// For more information take a look at `DelegateProxyType`. open class RxTabBarControllerDelegateProxy : DelegateProxy , DelegateProxyType , UITabBarControllerDelegate { /// Typed parent object. public weak private(set) var tabBar: UITabBarController? /// - parameter tabBar: Parent object for delegate proxy. public init(tabBar: ParentObject) { self.tabBar = tabBar super.init(parentObject: tabBar, delegateProxy: RxTabBarControllerDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxTabBarControllerDelegateProxy(tabBar: $0) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTabBarDelegateProxy.swift ================================================ // // RxTabBarDelegateProxy.swift // RxCocoa // // Created by Jesse Farless on 5/14/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UITabBar: HasDelegate { public typealias Delegate = UITabBarDelegate } /// For more information take a look at `DelegateProxyType`. open class RxTabBarDelegateProxy : DelegateProxy , DelegateProxyType , UITabBarDelegate { /// Typed parent object. public weak private(set) var tabBar: UITabBar? /// - parameter tabBar: Parent object for delegate proxy. public init(tabBar: ParentObject) { self.tabBar = tabBar super.init(parentObject: tabBar, delegateProxy: RxTabBarDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxTabBarDelegateProxy(tabBar: $0) } } /// For more information take a look at `DelegateProxyType`. open class func currentDelegate(for object: ParentObject) -> UITabBarDelegate? { return object.delegate } /// For more information take a look at `DelegateProxyType`. open class func setCurrentDelegate(_ delegate: UITabBarDelegate?, to object: ParentObject) { object.delegate = delegate } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDataSourceProxy.swift ================================================ // // RxTableViewDataSourceProxy.swift // RxCocoa // // Created by Krunoslav Zaher on 6/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UITableView: HasDataSource { public typealias DataSource = UITableViewDataSource } let tableViewDataSourceNotSet = TableViewDataSourceNotSet() final class TableViewDataSourceNotSet : NSObject , UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { rxAbstractMethod(message: dataSourceNotSet) } } /// For more information take a look at `DelegateProxyType`. open class RxTableViewDataSourceProxy : DelegateProxy , DelegateProxyType , UITableViewDataSource { /// Typed parent object. public weak private(set) var tableView: UITableView? /// - parameter tableView: Parent object for delegate proxy. public init(tableView: UITableView) { self.tableView = tableView super.init(parentObject: tableView, delegateProxy: RxTableViewDataSourceProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxTableViewDataSourceProxy(tableView: $0) } } fileprivate weak var _requiredMethodsDataSource: UITableViewDataSource? = tableViewDataSourceNotSet // MARK: delegate /// Required delegate method implementation. public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return (_requiredMethodsDataSource ?? tableViewDataSourceNotSet).tableView(tableView, numberOfRowsInSection: section) } /// Required delegate method implementation. public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return (_requiredMethodsDataSource ?? tableViewDataSourceNotSet).tableView(tableView, cellForRowAt: indexPath) } /// For more information take a look at `DelegateProxyType`. open override func setForwardToDelegate(_ forwardToDelegate: UITableViewDataSource?, retainDelegate: Bool) { _requiredMethodsDataSource = forwardToDelegate ?? tableViewDataSourceNotSet super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift ================================================ // // RxTableViewDelegateProxy.swift // RxCocoa // // Created by Krunoslav Zaher on 6/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /// For more information take a look at `DelegateProxyType`. open class RxTableViewDelegateProxy : RxScrollViewDelegateProxy , UITableViewDelegate { /// Typed parent object. public weak private(set) var tableView: UITableView? /// - parameter tableView: Parent object for delegate proxy. public init(tableView: UITableView) { self.tableView = tableView super.init(scrollView: tableView) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTextStorageDelegateProxy.swift ================================================ // // RxTextStorageDelegateProxy.swift // RxCocoa // // Created by Segii Shulga on 12/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension NSTextStorage: HasDelegate { public typealias Delegate = NSTextStorageDelegate } open class RxTextStorageDelegateProxy : DelegateProxy , DelegateProxyType , NSTextStorageDelegate { /// Typed parent object. public weak private(set) var textStorage: NSTextStorage? /// - parameter textStorage: Parent object for delegate proxy. public init(textStorage: NSTextStorage) { self.textStorage = textStorage super.init(parentObject: textStorage, delegateProxy: RxTextStorageDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxTextStorageDelegateProxy(textStorage: $0) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift ================================================ // // RxTextViewDelegateProxy.swift // RxCocoa // // Created by Yuta ToKoRo on 7/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /// For more information take a look at `DelegateProxyType`. open class RxTextViewDelegateProxy : RxScrollViewDelegateProxy , UITextViewDelegate { /// Typed parent object. public weak private(set) var textView: UITextView? /// - parameter textview: Parent object for delegate proxy. public init(textView: UITextView) { self.textView = textView super.init(scrollView: textView) } // MARK: delegate methods /// For more information take a look at `DelegateProxyType`. @objc open func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { /** We've had some issues with observing text changes. This is here just in case we need the same hack in future and that we wouldn't need to change the public interface. */ let forwardToDelegate = self.forwardToDelegate() as? UITextViewDelegate return forwardToDelegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) ?? true } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/Proxies/RxWebViewDelegateProxy.swift ================================================ // // RxWebViewDelegateProxy.swift // RxCocoa // // Created by Andrew Breckenridge on 9/26/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension UIWebView: HasDelegate { public typealias Delegate = UIWebViewDelegate } open class RxWebViewDelegateProxy : DelegateProxy , DelegateProxyType , UIWebViewDelegate { /// Typed parent object. public weak private(set) var webView: UIWebView? /// - parameter webView: Parent object for delegate proxy. public init(webView: ParentObject) { self.webView = webView super.init(parentObject: webView, delegateProxy: RxWebViewDelegateProxy.self) } // Register known implementations public static func registerKnownImplementations() { self.register { RxWebViewDelegateProxy(webView: $0) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIActivityIndicatorView+Rx.swift ================================================ // // UIActivityIndicatorView+Rx.swift // RxCocoa // // Created by Ivan Persidskiy on 02/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIActivityIndicatorView { /// Bindable sink for `startAnimating()`, `stopAnimating()` methods. public var isAnimating: Binder { return Binder(self.base) { activityIndicator, active in if active { activityIndicator.startAnimating() } else { activityIndicator.stopAnimating() } } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIAlertAction+Rx.swift ================================================ // // UIAlertAction+Rx.swift // RxCocoa // // Created by Andrew Breckenridge on 5/7/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIAlertAction { /// Bindable sink for `enabled` property. public var isEnabled: Binder { return Binder(self.base) { alertAction, value in alertAction.isEnabled = value } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIApplication+Rx.swift ================================================ // // UIApplication+Rx.swift // RxCocoa // // Created by Mads Bøgeskov on 18/01/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIApplication { /// Bindable sink for `networkActivityIndicatorVisible`. public var isNetworkActivityIndicatorVisible: Binder { return Binder(self.base) { application, active in application.isNetworkActivityIndicatorVisible = active } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIBarButtonItem+Rx.swift ================================================ // // UIBarButtonItem+Rx.swift // RxCocoa // // Created by Daniel Tartaglia on 5/31/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif fileprivate var rx_tap_key: UInt8 = 0 extension Reactive where Base: UIBarButtonItem { /// Bindable sink for `enabled` property. public var isEnabled: Binder { return Binder(self.base) { element, value in element.isEnabled = value } } /// Bindable sink for `title` property. public var title: Binder { return Binder(self.base) { element, value in element.title = value } } /// Reactive wrapper for target action pattern on `self`. public var tap: ControlEvent<()> { let source = lazyInstanceObservable(&rx_tap_key) { () -> Observable<()> in Observable.create { [weak control = self.base] observer in guard let control = control else { observer.on(.completed) return Disposables.create() } let target = BarButtonItemTarget(barButtonItem: control) { observer.on(.next(())) } return target } .takeUntil(self.deallocated) .share() } return ControlEvent(events: source) } } @objc final class BarButtonItemTarget: RxTarget { typealias Callback = () -> Void weak var barButtonItem: UIBarButtonItem? var callback: Callback! init(barButtonItem: UIBarButtonItem, callback: @escaping () -> Void) { self.barButtonItem = barButtonItem self.callback = callback super.init() barButtonItem.target = self barButtonItem.action = #selector(BarButtonItemTarget.action(_:)) } override func dispose() { super.dispose() #if DEBUG MainScheduler.ensureExecutingOnScheduler() #endif barButtonItem?.target = nil barButtonItem?.action = nil callback = nil } @objc func action(_ sender: AnyObject) { callback() } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIButton+Rx.swift ================================================ // // UIButton+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 3/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIButton { /// Reactive wrapper for `TouchUpInside` control event. public var tap: ControlEvent { return controlEvent(.touchUpInside) } } #endif #if os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIButton { /// Reactive wrapper for `PrimaryActionTriggered` control event. public var primaryAction: ControlEvent { return controlEvent(.primaryActionTriggered) } } #endif #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIButton { /// Reactive wrapper for `setTitle(_:for:)` public func title(for controlState: UIControlState = []) -> Binder { return Binder(self.base) { (button, title) -> () in button.setTitle(title, for: controlState) } } /// Reactive wrapper for `setImage(_:for:)` public func image(for controlState: UIControlState = []) -> Binder { return Binder(self.base) { (button, image) -> () in button.setImage(image, for: controlState) } } /// Reactive wrapper for `setBackgroundImage(_:for:)` public func backgroundImage(for controlState: UIControlState = []) -> Binder { return Binder(self.base) { (button, image) -> () in button.setBackgroundImage(image, for: controlState) } } } #endif #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIButton { /// Reactive wrapper for `setAttributedTitle(_:controlState:)` public func attributedTitle(for controlState: UIControlState = []) -> Binder { return Binder(self.base) { (button, attributedTitle) -> () in button.setAttributedTitle(attributedTitle, for: controlState) } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UICollectionView+Rx.swift ================================================ // // UICollectionView+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 4/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit // Items extension Reactive where Base: UICollectionView { /** Binds sequences of elements to collection view items. - parameter source: Observable sequence of items. - parameter cellFactory: Transform between sequence elements and view cells. - returns: Disposable object that can be used to unbind. Example let items = Observable.just([ 1, 2, 3 ]) items .bind(to: collectionView.rx.items) { (collectionView, row, element) in let indexPath = IndexPath(row: row, section: 0) let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell cell.value?.text = "\(element) @ \(row)" return cell } .disposed(by: disposeBag) */ public func items (_ source: O) -> (_ cellFactory: @escaping (UICollectionView, Int, S.Iterator.Element) -> UICollectionViewCell) -> Disposable where O.E == S { return { cellFactory in let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper(cellFactory: cellFactory) return self.items(dataSource: dataSource)(source) } } /** Binds sequences of elements to collection view items. - parameter cellIdentifier: Identifier used to dequeue cells. - parameter source: Observable sequence of items. - parameter configureCell: Transform between sequence elements and view cells. - parameter cellType: Type of table view cell. - returns: Disposable object that can be used to unbind. Example let items = Observable.just([ 1, 2, 3 ]) items .bind(to: collectionView.rx.items(cellIdentifier: "Cell", cellType: NumberCell.self)) { (row, element, cell) in cell.value?.text = "\(element) @ \(row)" } .disposed(by: disposeBag) */ public func items (cellIdentifier: String, cellType: Cell.Type = Cell.self) -> (_ source: O) -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) -> Disposable where O.E == S { return { source in return { configureCell in let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper { (cv, i, item) in let indexPath = IndexPath(item: i, section: 0) let cell = cv.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! Cell configureCell(i, item, cell) return cell } return self.items(dataSource: dataSource)(source) } } } /** Binds sequences of elements to collection view items using a custom reactive data used to perform the transformation. - parameter dataSource: Data source used to transform elements to view cells. - parameter source: Observable sequence of items. - returns: Disposable object that can be used to unbind. Example let dataSource = RxCollectionViewSectionedReloadDataSource>() let items = Observable.just([ SectionModel(model: "First section", items: [ 1.0, 2.0, 3.0 ]), SectionModel(model: "Second section", items: [ 1.0, 2.0, 3.0 ]), SectionModel(model: "Third section", items: [ 1.0, 2.0, 3.0 ]) ]) dataSource.configureCell = { (dataSource, cv, indexPath, element) in let cell = cv.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell cell.value?.text = "\(element) @ row \(indexPath.row)" return cell } items .bind(to: collectionView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) */ public func items< DataSource: RxCollectionViewDataSourceType & UICollectionViewDataSource, O: ObservableType> (dataSource: DataSource) -> (_ source: O) -> Disposable where DataSource.Element == O.E { return { source in // This is called for sideeffects only, and to make sure delegate proxy is in place when // data source is being bound. // This is needed because theoretically the data source subscription itself might // call `self.rx.delegate`. If that happens, it might cause weird side effects since // setting data source will set delegate, and UICollectionView might get into a weird state. // Therefore it's better to set delegate proxy first, just to be sure. _ = self.delegate // Strong reference is needed because data source is in use until result subscription is disposed return source.subscribeProxyDataSource(ofObject: self.base, dataSource: dataSource, retainDataSource: true) { [weak collectionView = self.base] (_: RxCollectionViewDataSourceProxy, event) -> Void in guard let collectionView = collectionView else { return } dataSource.collectionView(collectionView, observedEvent: event) } } } } extension Reactive where Base: UICollectionView { public typealias DisplayCollectionViewCellEvent = (cell: UICollectionViewCell, at: IndexPath) public typealias DisplayCollectionViewSupplementaryViewEvent = (supplementaryView: UICollectionReusableView, elementKind: String, at: IndexPath) /// Reactive wrapper for `dataSource`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var dataSource: DelegateProxy { return RxCollectionViewDataSourceProxy.proxy(for: base) } /// Installs data source as forwarding delegate on `rx.dataSource`. /// Data source won't be retained. /// /// It enables using normal delegate mechanism with reactive delegate mechanism. /// /// - parameter dataSource: Data source object. /// - returns: Disposable object that can be used to unbind the data source. public func setDataSource(_ dataSource: UICollectionViewDataSource) -> Disposable { return RxCollectionViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self.base) } /// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`. public var itemSelected: ControlEvent { let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didSelectItemAt:))) .map { a in return a[1] as! IndexPath } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`. public var itemDeselected: ControlEvent { let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didDeselectItemAt:))) .map { a in return a[1] as! IndexPath } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView(_:didHighlightItemAt:)`. public var itemHighlighted: ControlEvent { let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didHighlightItemAt:))) .map { a in return try castOrThrow(IndexPath.self, a[1]) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView(_:didUnhighlightItemAt:)`. public var itemUnhighlighted: ControlEvent { let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didUnhighlightItemAt:))) .map { a in return try castOrThrow(IndexPath.self, a[1]) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView:willDisplay:forItemAt:`. public var willDisplayCell: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:willDisplay:forItemAt:))) .map { a in return (try castOrThrow(UICollectionViewCell.self, a[1]), try castOrThrow(IndexPath.self, a[2])) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView(_:willDisplaySupplementaryView:forElementKind:at:)`. public var willDisplaySupplementaryView: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:willDisplaySupplementaryView:forElementKind:at:))) .map { a in return (try castOrThrow(UICollectionReusableView.self, a[1]), try castOrThrow(String.self, a[2]), try castOrThrow(IndexPath.self, a[3])) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView:didEndDisplaying:forItemAt:`. public var didEndDisplayingCell: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didEndDisplaying:forItemAt:))) .map { a in return (try castOrThrow(UICollectionViewCell.self, a[1]), try castOrThrow(IndexPath.self, a[2])) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView(_:didEndDisplayingSupplementaryView:forElementOfKind:at:)`. public var didEndDisplayingSupplementaryView: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didEndDisplayingSupplementaryView:forElementOfKind:at:))) .map { a in return (try castOrThrow(UICollectionReusableView.self, a[1]), try castOrThrow(String.self, a[2]), try castOrThrow(IndexPath.self, a[3])) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`. /// /// It can be only used when one of the `rx.itemsWith*` methods is used to bind observable sequence, /// or any other data source conforming to `SectionedViewDataSourceType` protocol. /// /// ``` /// collectionView.rx.modelSelected(MyModel.self) /// .map { ... /// ``` public func modelSelected(_ modelType: T.Type) -> ControlEvent { let source: Observable = itemSelected.flatMap { [weak view = self.base as UICollectionView] indexPath -> Observable in guard let view = view else { return Observable.empty() } return Observable.just(try view.rx.model(at: indexPath)) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`. /// /// It can be only used when one of the `rx.itemsWith*` methods is used to bind observable sequence, /// or any other data source conforming to `SectionedViewDataSourceType` protocol. /// /// ``` /// collectionView.rx.modelDeselected(MyModel.self) /// .map { ... /// ``` public func modelDeselected(_ modelType: T.Type) -> ControlEvent { let source: Observable = itemDeselected.flatMap { [weak view = self.base as UICollectionView] indexPath -> Observable in guard let view = view else { return Observable.empty() } return Observable.just(try view.rx.model(at: indexPath)) } return ControlEvent(events: source) } /// Synchronous helper method for retrieving a model at indexPath through a reactive data source public func model(at indexPath: IndexPath) throws -> T { let dataSource: SectionedViewDataSourceType = castOrFatalError(self.dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx.itemsWith*` methods was used.") let element = try dataSource.model(at: indexPath) return element as! T } } #endif #if os(tvOS) extension Reactive where Base: UICollectionView { /// Reactive wrapper for `delegate` message `collectionView(_:didUpdateFocusInContext:withAnimationCoordinator:)`. public var didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context: UICollectionViewFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator)> { let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didUpdateFocusIn:with:))) .map { a -> (context: UICollectionViewFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) in let context = a[1] as! UICollectionViewFocusUpdateContext let animationCoordinator = a[2] as! UIFocusAnimationCoordinator return (context: context, animationCoordinator: animationCoordinator) } return ControlEvent(events: source) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIControl+Rx.swift ================================================ // // UIControl+Rx.swift // RxCocoa // // Created by Daniel Tartaglia on 5/23/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIControl { /// Bindable sink for `enabled` property. public var isEnabled: Binder { return Binder(self.base) { control, value in control.isEnabled = value } } /// Bindable sink for `selected` property. public var isSelected: Binder { return Binder(self.base) { control, selected in control.isSelected = selected } } /// Reactive wrapper for target action pattern. /// /// - parameter controlEvents: Filter for observed event types. public func controlEvent(_ controlEvents: UIControlEvents) -> ControlEvent<()> { let source: Observable = Observable.create { [weak control = self.base] observer in MainScheduler.ensureExecutingOnScheduler() guard let control = control else { observer.on(.completed) return Disposables.create() } let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { control in observer.on(.next(())) } return Disposables.create(with: controlTarget.dispose) } .takeUntil(deallocated) return ControlEvent(events: source) } /// Creates a `ControlProperty` that is triggered by target/action pattern value updates. /// /// - parameter controlEvents: Events that trigger value update sequence elements. /// - parameter getter: Property value getter. /// - parameter setter: Property value setter. public func controlProperty( editingEvents: UIControlEvents, getter: @escaping (Base) -> T, setter: @escaping (Base, T) -> () ) -> ControlProperty { let source: Observable = Observable.create { [weak weakControl = base] observer in guard let control = weakControl else { observer.on(.completed) return Disposables.create() } observer.on(.next(getter(control))) let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in if let control = weakControl { observer.on(.next(getter(control))) } } return Disposables.create(with: controlTarget.dispose) } .takeUntil(deallocated) let bindingObserver = Binder(base, binding: setter) return ControlProperty(values: source, valueSink: bindingObserver) } /// This is a separate method is to better communicate to public consumers that /// an `editingEvent` needs to fire for control property to be updated. internal func controlPropertyWithDefaultEvents( editingEvents: UIControlEvents = [.allEditingEvents, .valueChanged], getter: @escaping (Base) -> T, setter: @escaping (Base, T) -> () ) -> ControlProperty { return controlProperty( editingEvents: [.allEditingEvents, .valueChanged], getter: getter, setter: setter ) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIDatePicker+Rx.swift ================================================ // // UIDatePicker+Rx.swift // RxCocoa // // Created by Daniel Tartaglia on 5/31/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIDatePicker { /// Reactive wrapper for `date` property. public var date: ControlProperty { return value } /// Reactive wrapper for `date` property. public var value: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { datePicker in datePicker.date }, setter: { datePicker, value in datePicker.date = value } ) } /// Reactive wrapper for `countDownDuration` property. public var countDownDuration: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { datePicker in datePicker.countDownDuration }, setter: { datePicker, value in datePicker.countDownDuration = value } ) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIGestureRecognizer+Rx.swift ================================================ // // UIGestureRecognizer+Rx.swift // RxCocoa // // Created by Carlos García on 10/6/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif // This should be only used from `MainScheduler` final class GestureTarget: RxTarget { typealias Callback = (Recognizer) -> Void let selector = #selector(ControlTarget.eventHandler(_:)) weak var gestureRecognizer: Recognizer? var callback: Callback? init(_ gestureRecognizer: Recognizer, callback: @escaping Callback) { self.gestureRecognizer = gestureRecognizer self.callback = callback super.init() gestureRecognizer.addTarget(self, action: selector) let method = self.method(for: selector) if method == nil { fatalError("Can't find method") } } @objc func eventHandler(_ sender: UIGestureRecognizer) { if let callback = self.callback, let gestureRecognizer = self.gestureRecognizer { callback(gestureRecognizer) } } override func dispose() { super.dispose() self.gestureRecognizer?.removeTarget(self, action: self.selector) self.callback = nil } } extension Reactive where Base: UIGestureRecognizer { /// Reactive wrapper for gesture recognizer events. public var event: ControlEvent { let source: Observable = Observable.create { [weak control = self.base] observer in MainScheduler.ensureExecutingOnScheduler() guard let control = control else { observer.on(.completed) return Disposables.create() } let observer = GestureTarget(control) { control in observer.on(.next(control)) } return observer }.takeUntil(deallocated) return ControlEvent(events: source) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIImageView+Rx.swift ================================================ // // UIImageView+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 4/1/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIImageView { /// Bindable sink for `image` property. public var image: Binder { return Binder(base) { imageView, image in imageView.image = image } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UILabel+Rx.swift ================================================ // // UILabel+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 4/1/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UILabel { /// Bindable sink for `text` property. public var text: Binder { return Binder(self.base) { label, text in label.text = text } } /// Bindable sink for `attributedText` property. public var attributedText: Binder { return Binder(self.base) { label, text in label.attributedText = text } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UINavigationController+Rx.swift ================================================ // // UINavigationController+Rx.swift // RxCocoa // // Created by Diogo on 13/04/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UINavigationController { public typealias ShowEvent = (viewController: UIViewController, animated: Bool) /// Reactive wrapper for `delegate`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxNavigationControllerDelegateProxy.proxy(for: base) } /// Reactive wrapper for delegate method `navigationController(:willShow:animated:)`. public var willShow: ControlEvent { let source: Observable = delegate .methodInvoked(#selector(UINavigationControllerDelegate.navigationController(_:willShow:animated:))) .map { arg in let viewController = try castOrThrow(UIViewController.self, arg[1]) let animated = try castOrThrow(Bool.self, arg[2]) return (viewController, animated) } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `navigationController(:didShow:animated:)`. public var didShow: ControlEvent { let source: Observable = delegate .methodInvoked(#selector(UINavigationControllerDelegate.navigationController(_:didShow:animated:))) .map { arg in let viewController = try castOrThrow(UIViewController.self, arg[1]) let animated = try castOrThrow(Bool.self, arg[2]) return (viewController, animated) } return ControlEvent(events: source) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UINavigationItem+Rx.swift ================================================ // // UINavigationItem+Rx.swift // RxCocoa // // Created by kumapo on 2016/05/09. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UINavigationItem { /// Bindable sink for `title` property. public var title: Binder { return Binder(self.base) { navigationItem, text in navigationItem.title = text } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIPageControl+Rx.swift ================================================ // // UIPageControl+Rx.swift // RxCocoa // // Created by Francesco Puntillo on 14/04/2016. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIPageControl { /// Bindable sink for `currentPage` property. public var currentPage: Binder { return Binder(self.base) { controller, page in controller.currentPage = page } } /// Bindable sink for `numberOfPages` property. public var numberOfPages: Binder { return Binder(self.base) { controller, page in controller.numberOfPages = page } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIPickerView+Rx.swift ================================================ // // UIPickerView+Rx.swift // RxCocoa // // Created by Segii Shulga on 5/12/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIPickerView { /// Reactive wrapper for `delegate`. /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxPickerViewDelegateProxy.proxy(for: base) } /// Installs delegate as forwarding delegate on `delegate`. /// Delegate won't be retained. /// /// It enables using normal delegate mechanism with reactive delegate mechanism. /// /// - parameter delegate: Delegate object. /// - returns: Disposable object that can be used to unbind the delegate. public func setDelegate(_ delegate: UIPickerViewDelegate) -> Disposable { return RxPickerViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base) } /** Reactive wrapper for `dataSource`. For more information take a look at `DelegateProxyType` protocol documentation. */ public var dataSource: DelegateProxy { return RxPickerViewDataSourceProxy.proxy(for: base) } /** Reactive wrapper for `delegate` message `pickerView:didSelectRow:inComponent:`. */ public var itemSelected: ControlEvent<(row: Int, component: Int)> { let source = delegate .methodInvoked(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) .map { return (row: try castOrThrow(Int.self, $0[1]), component: try castOrThrow(Int.self, $0[2])) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `pickerView:didSelectRow:inComponent:`. It can be only used when one of the `rx.itemTitles, rx.itemAttributedTitles, items(_ source: O)` methods is used to bind observable sequence, or any other data source conforming to a `ViewDataSourceType` protocol. ``` pickerView.rx.modelSelected(MyModel.self) .map { ... ``` - parameter modelType: Type of a Model which bound to the dataSource */ public func modelSelected(_ modelType: T.Type) -> ControlEvent<[T]> { let source = itemSelected.flatMap { [weak view = self.base as UIPickerView] (_, component) -> Observable<[T]> in guard let view = view else { return Observable.empty() } let model: [T] = try (0 ..< view.numberOfComponents).map { component in let row = view.selectedRow(inComponent: component) return try view.rx.model(at: IndexPath(row: row, section: component)) } return Observable.just(model) } return ControlEvent(events: source) } /** Binds sequences of elements to picker view rows. - parameter source: Observable sequence of items. - parameter titleForRow: Transform between sequence elements and row titles. - returns: Disposable object that can be used to unbind. Example: let items = Observable.just([ "First Item", "Second Item", "Third Item" ]) items .bind(to: pickerView.rx.itemTitles) { (row, element) in return element.title } .disposed(by: disposeBag) */ public func itemTitles (_ source: O) -> (_ titleForRow: @escaping (Int, S.Iterator.Element) -> String?) -> Disposable where O.E == S { return { titleForRow in let adapter = RxStringPickerViewAdapter(titleForRow: titleForRow) return self.items(adapter: adapter)(source) } } /** Binds sequences of elements to picker view rows. - parameter source: Observable sequence of items. - parameter attributedTitleForRow: Transform between sequence elements and row attributed titles. - returns: Disposable object that can be used to unbind. Example: let items = Observable.just([ "First Item", "Second Item", "Third Item" ]) items .bind(to: pickerView.rx.itemAttributedTitles) { (row, element) in return NSAttributedString(string: element.title) } .disposed(by: disposeBag) */ public func itemAttributedTitles (_ source: O) -> (_ attributedTitleForRow: @escaping (Int, S.Iterator.Element) -> NSAttributedString?) -> Disposable where O.E == S { return { attributedTitleForRow in let adapter = RxAttributedStringPickerViewAdapter(attributedTitleForRow: attributedTitleForRow) return self.items(adapter: adapter)(source) } } /** Binds sequences of elements to picker view rows. - parameter source: Observable sequence of items. - parameter viewForRow: Transform between sequence elements and row views. - returns: Disposable object that can be used to unbind. Example: let items = Observable.just([ "First Item", "Second Item", "Third Item" ]) items .bind(to: pickerView.rx.items) { (row, element, view) in guard let myView = view as? MyView else { let view = MyView() view.configure(with: element) return view } myView.configure(with: element) return myView } .disposed(by: disposeBag) */ public func items (_ source: O) -> (_ viewForRow: @escaping (Int, S.Iterator.Element, UIView?) -> UIView) -> Disposable where O.E == S { return { viewForRow in let adapter = RxPickerViewAdapter(viewForRow: viewForRow) return self.items(adapter: adapter)(source) } } /** Binds sequences of elements to picker view rows using a custom reactive adapter used to perform the transformation. This method will retain the adapter for as long as the subscription isn't disposed (result `Disposable` being disposed). In case `source` observable sequence terminates successfully, the adapter will present latest element until the subscription isn't disposed. - parameter adapter: Adapter used to transform elements to picker components. - parameter source: Observable sequence of items. - returns: Disposable object that can be used to unbind. */ public func items(adapter: Adapter) -> (_ source: O) -> Disposable where O.E == Adapter.Element { return { source in let delegateSubscription = self.setDelegate(adapter) let dataSourceSubscription = source.subscribeProxyDataSource(ofObject: self.base, dataSource: adapter, retainDataSource: true, binding: { [weak pickerView = self.base] (_: RxPickerViewDataSourceProxy, event) in guard let pickerView = pickerView else { return } adapter.pickerView(pickerView, observedEvent: event) }) return Disposables.create(delegateSubscription, dataSourceSubscription) } } /** Synchronous helper method for retrieving a model at indexPath through a reactive data source. */ public func model(at indexPath: IndexPath) throws -> T { let dataSource: SectionedViewDataSourceType = castOrFatalError(self.dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx.itemTitles, rx.itemAttributedTitles, items(_ source: O)` methods was used.") return castOrFatalError(try dataSource.model(at: indexPath)) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIProgressView+Rx.swift ================================================ // // UIProgressView+Rx.swift // RxCocoa // // Created by Samuel Bae on 2/27/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIProgressView { /// Bindable sink for `progress` property public var progress: Binder { return Binder(self.base) { progressView, progress in progressView.progress = progress } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIRefreshControl+Rx.swift ================================================ // // UIRefreshControl+Rx.swift // RxCocoa // // Created by Yosuke Ishikawa on 1/31/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIRefreshControl { /// Bindable sink for `beginRefreshing()`, `endRefreshing()` methods. public var isRefreshing: Binder { return Binder(self.base) { refreshControl, refresh in if refresh { refreshControl.beginRefreshing() } else { refreshControl.endRefreshing() } } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIScrollView+Rx.swift ================================================ // // UIScrollView+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 4/3/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UIScrollView { public typealias EndZoomEvent = (view: UIView?, scale: CGFloat) public typealias WillEndDraggingEvent = (velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) /// Reactive wrapper for `delegate`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxScrollViewDelegateProxy.proxy(for: base) } /// Reactive wrapper for `contentOffset`. public var contentOffset: ControlProperty { let proxy = RxScrollViewDelegateProxy.proxy(for: base) let bindingObserver = Binder(self.base) { scrollView, contentOffset in scrollView.contentOffset = contentOffset } return ControlProperty(values: proxy.contentOffsetBehaviorSubject, valueSink: bindingObserver) } /// Bindable sink for `scrollEnabled` property. public var isScrollEnabled: Binder { return Binder(self.base) { scrollView, scrollEnabled in scrollView.isScrollEnabled = scrollEnabled } } /// Reactive wrapper for delegate method `scrollViewDidScroll` public var didScroll: ControlEvent { let source = RxScrollViewDelegateProxy.proxy(for: base).contentOffsetPublishSubject return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillBeginDecelerating` public var willBeginDecelerating: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndDecelerating` public var didEndDecelerating: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndDecelerating(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillBeginDragging` public var willBeginDragging: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDragging(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)` public var willEndDragging: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillEndDragging(_:withVelocity:targetContentOffset:))) .map { value -> WillEndDraggingEvent in let velocity = try castOrThrow(CGPoint.self, value[1]) let targetContentOffsetValue = try castOrThrow(NSValue.self, value[2]) guard let rawPointer = targetContentOffsetValue.pointerValue else { throw RxCocoaError.unknown } let typedPointer = rawPointer.bindMemory(to: CGPoint.self, capacity: MemoryLayout.size) return (velocity, typedPointer) } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndDragging(_:willDecelerate:)` public var didEndDragging: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndDragging(_:willDecelerate:))).map { value -> Bool in return try castOrThrow(Bool.self, value[1]) } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidZoom` public var didZoom: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidZoom)).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidScrollToTop` public var didScrollToTop: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidScrollToTop(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndScrollingAnimation` public var didEndScrollingAnimation: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillBeginZooming(_:with:)` public var willBeginZooming: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginZooming(_:with:))).map { value -> UIView? in return try castOptionalOrThrow(UIView.self, value[1] as AnyObject) } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndZooming(_:with:atScale:)` public var didEndZooming: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndZooming(_:with:atScale:))).map { value -> EndZoomEvent in return (try castOptionalOrThrow(UIView.self, value[1] as AnyObject), try castOrThrow(CGFloat.self, value[2])) } return ControlEvent(events: source) } /// Installs delegate as forwarding delegate on `delegate`. /// Delegate won't be retained. /// /// It enables using normal delegate mechanism with reactive delegate mechanism. /// /// - parameter delegate: Delegate object. /// - returns: Disposable object that can be used to unbind the delegate. public func setDelegate(_ delegate: UIScrollViewDelegate) -> Disposable { return RxScrollViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UISearchBar+Rx.swift ================================================ // // UISearchBar+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 3/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UISearchBar { /// Reactive wrapper for `delegate`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxSearchBarDelegateProxy.proxy(for: base) } /// Reactive wrapper for `text` property. public var text: ControlProperty { return value } /// Reactive wrapper for `text` property. public var value: ControlProperty { let source: Observable = Observable.deferred { [weak searchBar = self.base as UISearchBar] () -> Observable in let text = searchBar?.text return (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) ?? Observable.empty()) .map { a in return a[1] as? String } .startWith(text) } let bindingObserver = Binder(self.base) { (searchBar, text: String?) in searchBar.text = text } return ControlProperty(values: source, valueSink: bindingObserver) } /// Reactive wrapper for `selectedScopeButtonIndex` property. public var selectedScopeButtonIndex: ControlProperty { let source: Observable = Observable.deferred { [weak source = self.base as UISearchBar] () -> Observable in let index = source?.selectedScopeButtonIndex ?? 0 return (source?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:selectedScopeButtonIndexDidChange:))) ?? Observable.empty()) .map { a in return try castOrThrow(Int.self, a[1]) } .startWith(index) } let bindingObserver = Binder(self.base) { (searchBar, index: Int) in searchBar.selectedScopeButtonIndex = index } return ControlProperty(values: source, valueSink: bindingObserver) } #if os(iOS) /// Reactive wrapper for delegate method `searchBarCancelButtonClicked`. public var cancelButtonClicked: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarCancelButtonClicked(_:))) .map { _ in return () } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `searchBarBookmarkButtonClicked`. public var bookmarkButtonClicked: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarBookmarkButtonClicked(_:))) .map { _ in return () } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `searchBarResultsListButtonClicked`. public var resultsListButtonClicked: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarResultsListButtonClicked(_:))) .map { _ in return () } return ControlEvent(events: source) } #endif /// Reactive wrapper for delegate method `searchBarSearchButtonClicked`. public var searchButtonClicked: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarSearchButtonClicked(_:))) .map { _ in return () } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `searchBarTextDidBeginEditing`. public var textDidBeginEditing: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarTextDidBeginEditing(_:))) .map { _ in return () } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `searchBarTextDidEndEditing`. public var textDidEndEditing: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarTextDidEndEditing(_:))) .map { _ in return () } return ControlEvent(events: source) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UISearchController+Rx.swift ================================================ // // UISearchController+Rx.swift // RxCocoa // // Created by Segii Shulga on 3/17/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit @available(iOS 8.0, *) extension Reactive where Base: UISearchController { /// Reactive wrapper for `delegate`. /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxSearchControllerDelegateProxy.proxy(for: base) } /// Reactive wrapper for `delegate` message. public var didDismiss: Observable { return delegate .methodInvoked( #selector(UISearchControllerDelegate.didDismissSearchController(_:))) .map {_ in} } /// Reactive wrapper for `delegate` message. public var didPresent: Observable { return delegate .methodInvoked(#selector(UISearchControllerDelegate.didPresentSearchController(_:))) .map {_ in} } /// Reactive wrapper for `delegate` message. public var present: Observable { return delegate .methodInvoked( #selector(UISearchControllerDelegate.presentSearchController(_:))) .map {_ in} } /// Reactive wrapper for `delegate` message. public var willDismiss: Observable { return delegate .methodInvoked(#selector(UISearchControllerDelegate.willDismissSearchController(_:))) .map {_ in} } /// Reactive wrapper for `delegate` message. public var willPresent: Observable { return delegate .methodInvoked( #selector(UISearchControllerDelegate.willPresentSearchController(_:))) .map {_ in} } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UISegmentedControl+Rx.swift ================================================ // // UISegmentedControl+Rx.swift // RxCocoa // // Created by Carlos García on 8/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UISegmentedControl { /// Reactive wrapper for `selectedSegmentIndex` property. public var selectedSegmentIndex: ControlProperty { return value } /// Reactive wrapper for `selectedSegmentIndex` property. public var value: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { segmentedControl in segmentedControl.selectedSegmentIndex }, setter: { segmentedControl, value in segmentedControl.selectedSegmentIndex = value } ) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UISlider+Rx.swift ================================================ // // UISlider+Rx.swift // RxCocoa // // Created by Alexander van der Werff on 28/05/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UISlider { /// Reactive wrapper for `value` property. public var value: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { slider in slider.value }, setter: { slider, value in slider.value = value } ) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIStepper+Rx.swift ================================================ // // UIStepper+Rx.swift // RxCocoa // // Created by Yuta ToKoRo on 9/1/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIStepper { /// Reactive wrapper for `value` property. public var value: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { stepper in stepper.value }, setter: { stepper, value in stepper.value = value } ) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UISwitch+Rx.swift ================================================ // // UISwitch+Rx.swift // RxCocoa // // Created by Carlos García on 8/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UISwitch { /// Reactive wrapper for `isOn` property. public var isOn: ControlProperty { return value } /** Reactive wrapper for `isOn` property. **⚠️ Versions prior to iOS 10.2 were leaking `UISwitch`s, so on those versions underlying observable sequence won't complete when nothing holds a strong reference to UISwitch.⚠️** */ public var value: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { uiSwitch in uiSwitch.isOn }, setter: { uiSwitch, value in uiSwitch.isOn = value } ) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UITabBar+Rx.swift ================================================ // // UITabBar+Rx.swift // RxCocoa // // Created by Jesse Farless on 5/13/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /** iOS only */ #if os(iOS) extension Reactive where Base: UITabBar { /// Reactive wrapper for `delegate` message `tabBar(_:willBeginCustomizing:)`. public var willBeginCustomizing: ControlEvent<[UITabBarItem]> { let source = delegate.methodInvoked(#selector(UITabBarDelegate.tabBar(_:willBeginCustomizing:))) .map { a in return try castOrThrow([UITabBarItem].self, a[1]) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `tabBar(_:didBeginCustomizing:)`. public var didBeginCustomizing: ControlEvent<[UITabBarItem]> { let source = delegate.methodInvoked(#selector(UITabBarDelegate.tabBar(_:didBeginCustomizing:))) .map { a in return try castOrThrow([UITabBarItem].self, a[1]) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `tabBar(_:willEndCustomizing:changed:)`. public var willEndCustomizing: ControlEvent<([UITabBarItem], Bool)> { let source = delegate.methodInvoked(#selector(UITabBarDelegate.tabBar(_:willEndCustomizing:changed:))) .map { (a: [Any]) -> (([UITabBarItem], Bool)) in let items = try castOrThrow([UITabBarItem].self, a[1]) let changed = try castOrThrow(Bool.self, a[2]) return (items, changed) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `tabBar(_:didEndCustomizing:changed:)`. public var didEndCustomizing: ControlEvent<([UITabBarItem], Bool)> { let source = delegate.methodInvoked(#selector(UITabBarDelegate.tabBar(_:didEndCustomizing:changed:))) .map { (a: [Any]) -> (([UITabBarItem], Bool)) in let items = try castOrThrow([UITabBarItem].self, a[1]) let changed = try castOrThrow(Bool.self, a[2]) return (items, changed) } return ControlEvent(events: source) } } #endif /** iOS and tvOS */ extension Reactive where Base: UITabBar { /// Reactive wrapper for `delegate`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxTabBarDelegateProxy.proxy(for: base) } /// Reactive wrapper for `delegate` message `tabBar(_:didSelect:)`. public var didSelectItem: ControlEvent { let source = delegate.methodInvoked(#selector(UITabBarDelegate.tabBar(_:didSelect:))) .map { a in return try castOrThrow(UITabBarItem.self, a[1]) } return ControlEvent(events: source) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UITabBarController+Rx.swift ================================================ // // UITabBarController+Rx.swift // RxCocoa // // Created by Yusuke Kita on 2016/12/07. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif /** iOS only */ #if os(iOS) extension Reactive where Base: UITabBarController { /// Reactive wrapper for `delegate` message `tabBarController:willBeginCustomizing:`. public var willBeginCustomizing: ControlEvent<[UIViewController]> { let source = delegate.methodInvoked(#selector(UITabBarControllerDelegate.tabBarController(_:willBeginCustomizing:))) .map { a in return try castOrThrow([UIViewController].self, a[1]) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `tabBarController:willEndCustomizing:changed:`. public var willEndCustomizing: ControlEvent<(viewControllers: [UIViewController], changed: Bool)> { let source = delegate.methodInvoked(#selector(UITabBarControllerDelegate.tabBarController(_:willEndCustomizing:changed:))) .map { (a: [Any]) -> (viewControllers: [UIViewController], changed: Bool) in let viewControllers = try castOrThrow([UIViewController].self, a[1]) let changed = try castOrThrow(Bool.self, a[2]) return (viewControllers, changed) } return ControlEvent(events: source) } /// Reactive wrapper for `delegate` message `tabBarController:didEndCustomizing:changed:`. public var didEndCustomizing: ControlEvent<(viewControllers: [UIViewController], changed: Bool)> { let source = delegate.methodInvoked(#selector(UITabBarControllerDelegate.tabBarController(_:didEndCustomizing:changed:))) .map { (a: [Any]) -> (viewControllers: [UIViewController], changed: Bool) in let viewControllers = try castOrThrow([UIViewController].self, a[1]) let changed = try castOrThrow(Bool.self, a[2]) return (viewControllers, changed) } return ControlEvent(events: source) } } #endif /** iOS and tvOS */ extension Reactive where Base: UITabBarController { /// Reactive wrapper for `delegate`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxTabBarControllerDelegateProxy.proxy(for: base) } /// Reactive wrapper for `delegate` message `tabBarController:didSelect:`. public var didSelect: ControlEvent { let source = delegate.methodInvoked(#selector(UITabBarControllerDelegate.tabBarController(_:didSelect:))) .map { a in return try castOrThrow(UIViewController.self, a[1]) } return ControlEvent(events: source) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UITabBarItem+Rx.swift ================================================ // // UITabBarItem+Rx.swift // RxCocoa // // Created by Mateusz Derks on 04/03/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UITabBarItem { /// Bindable sink for `badgeValue` property. public var badgeValue: Binder { return Binder(self.base) { tabBarItem, badgeValue in tabBarItem.badgeValue = badgeValue } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UITableView+Rx.swift ================================================ // // UITableView+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 4/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit // Items extension Reactive where Base: UITableView { /** Binds sequences of elements to table view rows. - parameter source: Observable sequence of items. - parameter cellFactory: Transform between sequence elements and view cells. - returns: Disposable object that can be used to unbind. Example: let items = Observable.just([ "First Item", "Second Item", "Third Item" ]) items .bind(to: tableView.rx.items) { (tableView, row, element) in let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! cell.textLabel?.text = "\(element) @ row \(row)" return cell } .disposed(by: disposeBag) */ public func items (_ source: O) -> (_ cellFactory: @escaping (UITableView, Int, S.Iterator.Element) -> UITableViewCell) -> Disposable where O.E == S { return { cellFactory in let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper(cellFactory: cellFactory) return self.items(dataSource: dataSource)(source) } } /** Binds sequences of elements to table view rows. - parameter cellIdentifier: Identifier used to dequeue cells. - parameter source: Observable sequence of items. - parameter configureCell: Transform between sequence elements and view cells. - parameter cellType: Type of table view cell. - returns: Disposable object that can be used to unbind. Example: let items = Observable.just([ "First Item", "Second Item", "Third Item" ]) items .bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in cell.textLabel?.text = "\(element) @ row \(row)" } .disposed(by: disposeBag) */ public func items (cellIdentifier: String, cellType: Cell.Type = Cell.self) -> (_ source: O) -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) -> Disposable where O.E == S { return { source in return { configureCell in let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper { (tv, i, item) in let indexPath = IndexPath(item: i, section: 0) let cell = tv.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell configureCell(i, item, cell) return cell } return self.items(dataSource: dataSource)(source) } } } /** Binds sequences of elements to table view rows using a custom reactive data used to perform the transformation. This method will retain the data source for as long as the subscription isn't disposed (result `Disposable` being disposed). In case `source` observable sequence terminates successfully, the data source will present latest element until the subscription isn't disposed. - parameter dataSource: Data source used to transform elements to view cells. - parameter source: Observable sequence of items. - returns: Disposable object that can be used to unbind. Example let dataSource = RxTableViewSectionedReloadDataSource>() let items = Observable.just([ SectionModel(model: "First section", items: [ 1.0, 2.0, 3.0 ]), SectionModel(model: "Second section", items: [ 1.0, 2.0, 3.0 ]), SectionModel(model: "Third section", items: [ 1.0, 2.0, 3.0 ]) ]) dataSource.configureCell = { (dataSource, tv, indexPath, element) in let cell = tv.dequeueReusableCell(withIdentifier: "Cell")! cell.textLabel?.text = "\(element) @ row \(indexPath.row)" return cell } items .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) */ public func items< DataSource: RxTableViewDataSourceType & UITableViewDataSource, O: ObservableType> (dataSource: DataSource) -> (_ source: O) -> Disposable where DataSource.Element == O.E { return { source in // This is called for sideeffects only, and to make sure delegate proxy is in place when // data source is being bound. // This is needed because theoretically the data source subscription itself might // call `self.rx.delegate`. If that happens, it might cause weird side effects since // setting data source will set delegate, and UITableView might get into a weird state. // Therefore it's better to set delegate proxy first, just to be sure. _ = self.delegate // Strong reference is needed because data source is in use until result subscription is disposed return source.subscribeProxyDataSource(ofObject: self.base, dataSource: dataSource as UITableViewDataSource, retainDataSource: true) { [weak tableView = self.base] (_: RxTableViewDataSourceProxy, event) -> Void in guard let tableView = tableView else { return } dataSource.tableView(tableView, observedEvent: event) } } } } extension Reactive where Base: UITableView { /** Reactive wrapper for `dataSource`. For more information take a look at `DelegateProxyType` protocol documentation. */ public var dataSource: DelegateProxy { return RxTableViewDataSourceProxy.proxy(for: base) } /** Installs data source as forwarding delegate on `rx.dataSource`. Data source won't be retained. It enables using normal delegate mechanism with reactive delegate mechanism. - parameter dataSource: Data source object. - returns: Disposable object that can be used to unbind the data source. */ public func setDataSource(_ dataSource: UITableViewDataSource) -> Disposable { return RxTableViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self.base) } // events /** Reactive wrapper for `delegate` message `tableView:didSelectRowAtIndexPath:`. */ public var itemSelected: ControlEvent { let source = self.delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:didSelectRowAt:))) .map { a in return try castOrThrow(IndexPath.self, a[1]) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:didDeselectRowAtIndexPath:`. */ public var itemDeselected: ControlEvent { let source = self.delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:didDeselectRowAt:))) .map { a in return try castOrThrow(IndexPath.self, a[1]) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:accessoryButtonTappedForRowWithIndexPath:`. */ public var itemAccessoryButtonTapped: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:accessoryButtonTappedForRowWith:))) .map { a in return try castOrThrow(IndexPath.self, a[1]) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:commitEditingStyle:forRowAtIndexPath:`. */ public var itemInserted: ControlEvent { let source = self.dataSource.methodInvoked(#selector(UITableViewDataSource.tableView(_:commit:forRowAt:))) .filter { a in return UITableViewCellEditingStyle(rawValue: (try castOrThrow(NSNumber.self, a[1])).intValue) == .insert } .map { a in return (try castOrThrow(IndexPath.self, a[2])) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:commitEditingStyle:forRowAtIndexPath:`. */ public var itemDeleted: ControlEvent { let source = self.dataSource.methodInvoked(#selector(UITableViewDataSource.tableView(_:commit:forRowAt:))) .filter { a in return UITableViewCellEditingStyle(rawValue: (try castOrThrow(NSNumber.self, a[1])).intValue) == .delete } .map { a in return try castOrThrow(IndexPath.self, a[2]) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:moveRowAtIndexPath:toIndexPath:`. */ public var itemMoved: ControlEvent { let source: Observable = self.dataSource.methodInvoked(#selector(UITableViewDataSource.tableView(_:moveRowAt:to:))) .map { a in return (try castOrThrow(IndexPath.self, a[1]), try castOrThrow(IndexPath.self, a[2])) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:willDisplayCell:forRowAtIndexPath:`. */ public var willDisplayCell: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:willDisplay:forRowAt:))) .map { a in return (try castOrThrow(UITableViewCell.self, a[1]), try castOrThrow(IndexPath.self, a[2])) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:didEndDisplayingCell:forRowAtIndexPath:`. */ public var didEndDisplayingCell: ControlEvent { let source: Observable = self.delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:didEndDisplaying:forRowAt:))) .map { a in return (try castOrThrow(UITableViewCell.self, a[1]), try castOrThrow(IndexPath.self, a[2])) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:didSelectRowAtIndexPath:`. It can be only used when one of the `rx.itemsWith*` methods is used to bind observable sequence, or any other data source conforming to `SectionedViewDataSourceType` protocol. ``` tableView.rx.modelSelected(MyModel.self) .map { ... ``` */ public func modelSelected(_ modelType: T.Type) -> ControlEvent { let source: Observable = self.itemSelected.flatMap { [weak view = self.base as UITableView] indexPath -> Observable in guard let view = view else { return Observable.empty() } return Observable.just(try view.rx.model(at: indexPath)) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:didDeselectRowAtIndexPath:`. It can be only used when one of the `rx.itemsWith*` methods is used to bind observable sequence, or any other data source conforming to `SectionedViewDataSourceType` protocol. ``` tableView.rx.modelDeselected(MyModel.self) .map { ... ``` */ public func modelDeselected(_ modelType: T.Type) -> ControlEvent { let source: Observable = self.itemDeselected.flatMap { [weak view = self.base as UITableView] indexPath -> Observable in guard let view = view else { return Observable.empty() } return Observable.just(try view.rx.model(at: indexPath)) } return ControlEvent(events: source) } /** Reactive wrapper for `delegate` message `tableView:commitEditingStyle:forRowAtIndexPath:`. It can be only used when one of the `rx.itemsWith*` methods is used to bind observable sequence, or any other data source conforming to `SectionedViewDataSourceType` protocol. ``` tableView.rx.modelDeleted(MyModel.self) .map { ... ``` */ public func modelDeleted(_ modelType: T.Type) -> ControlEvent { let source: Observable = self.itemDeleted.flatMap { [weak view = self.base as UITableView] indexPath -> Observable in guard let view = view else { return Observable.empty() } return Observable.just(try view.rx.model(at: indexPath)) } return ControlEvent(events: source) } /** Synchronous helper method for retrieving a model at indexPath through a reactive data source. */ public func model(at indexPath: IndexPath) throws -> T { let dataSource: SectionedViewDataSourceType = castOrFatalError(self.dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx.items*` methods was used.") let element = try dataSource.model(at: indexPath) return castOrFatalError(element) } } #endif #if os(tvOS) extension Reactive where Base: UITableView { /** Reactive wrapper for `delegate` message `tableView:didUpdateFocusInContext:withAnimationCoordinator:`. */ public var didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context: UITableViewFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator)> { let source = delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:didUpdateFocusIn:with:))) .map { a -> (context: UITableViewFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) in let context = a[1] as! UITableViewFocusUpdateContext let animationCoordinator = try castOrThrow(UIFocusAnimationCoordinator.self, a[2]) return (context: context, animationCoordinator: animationCoordinator) } return ControlEvent(events: source) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UITextField+Rx.swift ================================================ // // UITextField+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) #if !RX_NO_MODULE import RxSwift #endif import UIKit extension Reactive where Base: UITextField { /// Reactive wrapper for `text` property. public var text: ControlProperty { return value } /// Reactive wrapper for `text` property. public var value: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { textField in textField.text }, setter: { textField, value in // This check is important because setting text value always clears control state // including marked text selection which is imporant for proper input // when IME input method is used. if textField.text != value { textField.text = value } } ) } /// Bindable sink for `attributedText` property. public var attributedText: ControlProperty { return base.rx.controlPropertyWithDefaultEvents( getter: { textField in textField.attributedText }, setter: { textField, value in // This check is important because setting text value always clears control state // including marked text selection which is imporant for proper input // when IME input method is used. if textField.attributedText != value { textField.attributedText = value } } ) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UITextView+Rx.swift ================================================ // // UITextView+Rx.swift // RxCocoa // // Created by Yuta ToKoRo on 7/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UITextView { /// Reactive wrapper for `text` property public var text: ControlProperty { return value } /// Reactive wrapper for `text` property. public var value: ControlProperty { let source: Observable = Observable.deferred { [weak textView = self.base] in let text = textView?.text let textChanged = textView?.textStorage // This project uses text storage notifications because // that's the only way to catch autocorrect changes // in all cases. Other suggestions are welcome. .rx.didProcessEditingRangeChangeInLength // This observe on is here because text storage // will emit event while process is not completely done, // so rebinding a value will cause an exception to be thrown. .observeOn(MainScheduler.asyncInstance) .map { _ in return textView?.textStorage.string } ?? Observable.empty() return textChanged .startWith(text) } let bindingObserver = Binder(self.base) { (textView, text: String?) in // This check is important because setting text value always clears control state // including marked text selection which is imporant for proper input // when IME input method is used. if textView.text != text { textView.text = text } } return ControlProperty(values: source, valueSink: bindingObserver) } /// Reactive wrapper for `attributedText` property. public var attributedText: ControlProperty { let source: Observable = Observable.deferred { [weak textView = self.base] in let attributedText = textView?.attributedText let textChanged: Observable = textView?.textStorage // This project uses text storage notifications because // that's the only way to catch autocorrect changes // in all cases. Other suggestions are welcome. .rx.didProcessEditingRangeChangeInLength // This observe on is here because attributedText storage // will emit event while process is not completely done, // so rebinding a value will cause an exception to be thrown. .observeOn(MainScheduler.asyncInstance) .map { _ in return textView?.attributedText } ?? Observable.empty() return textChanged .startWith(attributedText) } let bindingObserver = Binder(self.base) { (textView, attributedText: NSAttributedString?) in // This check is important because setting text value always clears control state // including marked text selection which is imporant for proper input // when IME input method is used. if textView.attributedText != attributedText { textView.attributedText = attributedText } } return ControlProperty(values: source, valueSink: bindingObserver) } /// Reactive wrapper for `delegate` message. public var didBeginEditing: ControlEvent<()> { return ControlEvent<()>(events: self.delegate.methodInvoked(#selector(UITextViewDelegate.textViewDidBeginEditing(_:))) .map { a in return () }) } /// Reactive wrapper for `delegate` message. public var didEndEditing: ControlEvent<()> { return ControlEvent<()>(events: self.delegate.methodInvoked(#selector(UITextViewDelegate.textViewDidEndEditing(_:))) .map { a in return () }) } /// Reactive wrapper for `delegate` message. public var didChange: ControlEvent<()> { return ControlEvent<()>(events: self.delegate.methodInvoked(#selector(UITextViewDelegate.textViewDidChange(_:))) .map { a in return () }) } /// Reactive wrapper for `delegate` message. public var didChangeSelection: ControlEvent<()> { return ControlEvent<()>(events: self.delegate.methodInvoked(#selector(UITextViewDelegate.textViewDidChangeSelection(_:))) .map { a in return () }) } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIView+Rx.swift ================================================ // // UIView+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 12/6/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIView { /// Bindable sink for `hidden` property. public var isHidden: Binder { return Binder(self.base) { view, hidden in view.isHidden = hidden } } /// Bindable sink for `alpha` property. public var alpha: Binder { return Binder(self.base) { view, alpha in view.alpha = alpha } } /// Bindable sink for `isUserInteractionEnabled` property. public var isUserInteractionEnabled: Binder { return Binder(self.base) { view, userInteractionEnabled in view.isUserInteractionEnabled = userInteractionEnabled } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIViewController+Rx.swift ================================================ // // UIViewController+Rx.swift // RxCocoa // // Created by Kyle Fuller on 27/05/2016. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIViewController { /// Bindable sink for `title`. public var title: Binder { return Binder(self.base) { viewController, title in viewController.title = title } } } #endif ================================================ FILE: Pods/RxCocoa/RxCocoa/iOS/UIWebView+Rx.swift ================================================ // // UIWebView+Rx.swift // RxCocoa // // Created by Andrew Breckenridge on 8/30/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) import UIKit #if !RX_NO_MODULE import RxSwift #endif extension Reactive where Base: UIWebView { /// Reactive wrapper for `delegate`. /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxWebViewDelegateProxy.proxy(for: base) } /// Reactive wrapper for `delegate` message. public var didStartLoad: Observable { return delegate .methodInvoked(#selector(UIWebViewDelegate.webViewDidStartLoad(_:))) .map {_ in} } /// Reactive wrapper for `delegate` message. public var didFinishLoad: Observable { return delegate .methodInvoked(#selector(UIWebViewDelegate.webViewDidFinishLoad(_:))) .map {_ in} } /// Reactive wrapper for `delegate` message. public var didFailLoad: Observable { return delegate .methodInvoked(#selector(UIWebViewDelegate.webView(_:didFailLoadWithError:))) .map { a in return try castOrThrow(Error.self, a[1]) } } } #endif ================================================ FILE: Pods/RxDataSources/LICENSE.md ================================================ MIT License Copyright (c) 2017 RxSwift Community 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. ================================================ FILE: Pods/RxDataSources/README.md ================================================ [![Travis CI](https://travis-ci.org/RxSwiftCommunity/RxDataSources.svg?branch=master)](https://travis-ci.org/RxSwiftCommunity/RxDataSources) Table and Collection view data sources ====================================== ## Features - [x] **O(N)** algorithm for calculating differences - the algorithm has the assumption that all sections and items are unique so there is no ambiguity - in case there is ambiguity, fallbacks automagically on non animated refresh - [x] it applies additional heuristics to send the least number of commands to sectioned view - even though the running time is linear, preferred number of sent commands is usually a lot less than linear - it is preferred (and possible) to cap the number of changes to some small number, and in case the number of changes grows towards linear, just do normal reload - [x] Supports **extending your item and section structures** - just extend your item with `IdentifiableType` and `Equatable`, and your section with `AnimatableSectionModelType` - [x] Supports all combinations of two level hierarchical animations for **both sections and items** - Section animations: Insert, Delete, Move - Item animations: Insert, Delete, Move, Reload (if old value is not equal to new value) - [x] Configurable animation types for `Insert`, `Reload` and `Delete` (Automatic, Fade, ...) - [x] Example app - [x] Randomized stress tests (example app) - [x] Supports editing out of the box (example app) - [x] Works with `UITableView` and `UICollectionView` ## Why Writing table and collection view data sources is tedious. There is a large number of delegate methods that need to be implemented for the simplest case possible. RxSwift helps alleviate some of the burden with a simple data binding mechanism: 1) Turn your data into an Observable sequence 2) Bind the data to the tableView/collectionView using one of: - `rx.items(dataSource:protocol)` - `rx.items(cellIdentifier:String)` - `rx.items(cellIdentifier:String:Cell.Type:_:)` - `rx.items(_:_:)` ```swift let data = Observable<[String]>.just(["first element", "second element", "third element"]) data.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in cell.textLabel?.text = model } .disposed(by: disposeBag) ``` This works well with simple data sets but does not handle cases where you need to bind complex data sets with multiples sections, or when you need to perform animations when adding/modifying/deleting items. These are precisely the use cases that RxDataSources helps solve. With RxDataSources, it is super easy to just write ```swift let dataSource = RxTableViewSectionedReloadDataSource>() Observable.just([SectionModel(model: "title", items: [1, 2, 3])]) .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) ``` ![RxDataSources example app](https://raw.githubusercontent.com/kzaher/rxswiftcontent/rxdatasources/RxDataSources.gif) ## How Given the following custom data structure: ```swift struct CustomData { var anInt: Int var aString: String var aCGPoint: CGPoint } ``` 1) Start by defining your sections with a struct that conforms to the `SectionModelType` protocol: - define the `Item` typealias: equal to the type of items that the section will contain - declare an `items` property: of type array of `Item` ```swift struct SectionOfCustomData { var header: String var items: [Item] } extension SectionOfCustomData: SectionModelType { typealias Item = CustomData init(original: SectionOfCustomData, items: [Item]) { self = original self.items = items } } ``` 2) Create a dataSource object and pass it your `SectionOfCustomData` type: ```swift let dataSource = RxTableViewSectionedReloadDataSource() ``` 3) Customize closures on the dataSource as needed: - `configureCell` (required) - `titleForHeaderInSection` - `titleForFooterInSection` - etc ```swift dataSource.configureCell = { (ds: RxTableViewSectionedReloadDataSource, tv: UITableView, ip: IndexPath, item: Item) in let cell = tv.dequeueReusableCell(withIdentifier: "Cell", for: ip) cell.textLabel?.text = "Item \(item.anInt): \(item.aString) - \(item.aCGPoint.x):\(item.aCGPoint.y)" return cell } dataSource.titleForHeaderInSection = { ds, index in return ds.sectionModels[index].header } ``` 4) Define the actual data as an Observable sequence of CustomData objects and bind it to the tableView ```swift let sections = [ SectionOfCustomData(header: "First section", items: [CustomData(anInt: 0, aString: "zero", aCGPoint: CGPoint.zero), CustomData(anInt: 1, aString: "one", aCGPoint: CGPoint(x: 1, y: 1)) ]), SectionOfCustomData(header: "Second section", items: [CustomData(anInt: 2, aString: "two", aCGPoint: CGPoint(x: 2, y: 2)), CustomData(anInt: 3, aString: "three", aCGPoint: CGPoint(x: 3, y: 3)) ]) ] Observable.just(sections) .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag) ``` ### Animations To implement animations with RxDataSources, the same steps are required as with non-animated data, execept: - SectionOfCustomData needs to conform to `AnimatableSectionModelType` - dataSource needs to be an instance of `RxTableViewSectionedAnimatedDataSource` or `RxCollectionViewSectionedAnimatedDataSource` ## Requirements Xcode 9.0 Swift 4.0 For Swift 3.x version please use versions `1.0 ... 2.0.2` For Swift 2.3 version please use versions `0.1 ... 0.9` ## Installation **We'll try to keep the API as stable as possible, but breaking API changes can occur.** ### CocoaPods Podfile ``` pod 'RxDataSources', '~> 3.0' ``` ### Carthage Cartfile ``` github "RxSwiftCommunity/RxDataSources" ~> 3.0 ``` ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/AnimationConfiguration.swift ================================================ // // AnimationConfiguration.swift // RxDataSources // // Created by Esteban Torres on 5/2/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit /** Exposes custom animation styles for insertion, deletion and reloading behavior. */ public struct AnimationConfiguration { public let insertAnimation: UITableViewRowAnimation public let reloadAnimation: UITableViewRowAnimation public let deleteAnimation: UITableViewRowAnimation public init(insertAnimation: UITableViewRowAnimation = .automatic, reloadAnimation: UITableViewRowAnimation = .automatic, deleteAnimation: UITableViewRowAnimation = .automatic) { self.insertAnimation = insertAnimation self.reloadAnimation = reloadAnimation self.deleteAnimation = deleteAnimation } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/Array+Extensions.swift ================================================ // // Array+Extensions.swift // RxDataSources // // Created by Krunoslav Zaher on 4/26/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation extension Array where Element: SectionModelType { mutating func moveFromSourceIndexPath(_ sourceIndexPath: IndexPath, destinationIndexPath: IndexPath) { let sourceSection = self[sourceIndexPath.section] var sourceItems = sourceSection.items let sourceItem = sourceItems.remove(at: sourceIndexPath.item) let sourceSectionNew = Element(original: sourceSection, items: sourceItems) self[sourceIndexPath.section] = sourceSectionNew let destinationSection = self[destinationIndexPath.section] var destinationItems = destinationSection.items destinationItems.insert(sourceItem, at: destinationIndexPath.item) self[destinationIndexPath.section] = Element(original: destinationSection, items: destinationItems) } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/CollectionViewSectionedDataSource.swift ================================================ // // CollectionViewSectionedDataSource.swift // RxDataSources // // Created by Krunoslav Zaher on 7/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit #if !RX_NO_MODULE import RxCocoa #endif import Differentiator open class CollectionViewSectionedDataSource : NSObject , UICollectionViewDataSource , SectionedViewDataSourceType { public typealias I = S.Item public typealias Section = S public typealias ConfigureCell = (CollectionViewSectionedDataSource, UICollectionView, IndexPath, I) -> UICollectionViewCell public typealias ConfigureSupplementaryView = (CollectionViewSectionedDataSource, UICollectionView, String, IndexPath) -> UICollectionReusableView public typealias MoveItem = (CollectionViewSectionedDataSource, _ sourceIndexPath:IndexPath, _ destinationIndexPath:IndexPath) -> Void public typealias CanMoveItemAtIndexPath = (CollectionViewSectionedDataSource, IndexPath) -> Bool public init( configureCell: @escaping ConfigureCell, configureSupplementaryView: ConfigureSupplementaryView? = nil, moveItem: @escaping MoveItem = { _, _, _ in () }, canMoveItemAtIndexPath: @escaping CanMoveItemAtIndexPath = { _, _ in false } ) { self.configureCell = configureCell self.configureSupplementaryView = configureSupplementaryView self.moveItem = moveItem self.canMoveItemAtIndexPath = canMoveItemAtIndexPath } #if DEBUG // If data source has already been bound, then mutating it // afterwards isn't something desired. // This simulates immutability after binding var _dataSourceBound: Bool = false private func ensureNotMutatedAfterBinding() { assert(!_dataSourceBound, "Data source is already bound. Please write this line before binding call (`bindTo`, `drive`). Data source must first be completely configured, and then bound after that, otherwise there could be runtime bugs, glitches, or partial malfunctions.") } #endif // This structure exists because model can be mutable // In that case current state value should be preserved. // The state that needs to be preserved is ordering of items in section // and their relationship with section. // If particular item is mutable, that is irrelevant for this logic to function // properly. public typealias SectionModelSnapshot = SectionModel private var _sectionModels: [SectionModelSnapshot] = [] open var sectionModels: [S] { return _sectionModels.map { Section(original: $0.model, items: $0.items) } } open subscript(section: Int) -> S { let sectionModel = self._sectionModels[section] return S(original: sectionModel.model, items: sectionModel.items) } open subscript(indexPath: IndexPath) -> I { get { return self._sectionModels[indexPath.section].items[indexPath.item] } set(item) { var section = self._sectionModels[indexPath.section] section.items[indexPath.item] = item self._sectionModels[indexPath.section] = section } } open func model(at indexPath: IndexPath) throws -> Any { return self[indexPath] } open func setSections(_ sections: [S]) { self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) } } open var configureCell: ConfigureCell { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var configureSupplementaryView: ConfigureSupplementaryView? { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var moveItem: MoveItem { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var canMoveItemAtIndexPath: ((CollectionViewSectionedDataSource, IndexPath) -> Bool)? { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } // UICollectionViewDataSource open func numberOfSections(in collectionView: UICollectionView) -> Int { return _sectionModels.count } open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return _sectionModels[section].items.count } open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { precondition(indexPath.item < _sectionModels[indexPath.section].items.count) return configureCell(self, collectionView, indexPath, self[indexPath]) } open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { return configureSupplementaryView!(self, collectionView, kind, indexPath) } open func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool { guard let canMoveItem = canMoveItemAtIndexPath?(self, indexPath) else { return false } return canMoveItem } open func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath) self.moveItem(self, sourceIndexPath, destinationIndexPath) } override open func responds(to aSelector: Selector!) -> Bool { if aSelector == #selector(UICollectionViewDataSource.collectionView(_:viewForSupplementaryElementOfKind:at:)) { return configureSupplementaryView != nil } else { return super.responds(to: aSelector) } } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/DataSources.swift ================================================ // // DataSources.swift // RxDataSources // // Created by Krunoslav Zaher on 1/8/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation @_exported import Differentiator enum RxDataSourceError : Error { case preconditionFailed(message: String) } func rxPrecondition(_ condition: Bool, _ message: @autoclosure() -> String) throws -> () { if condition { return } rxDebugFatalError("Precondition failed") throw RxDataSourceError.preconditionFailed(message: message()) } func rxDebugFatalError(_ error: Error) { rxDebugFatalError("\(error)") } func rxDebugFatalError(_ message: String) { #if DEBUG fatalError(message) #else print(message) #endif } ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/Deprecated.swift ================================================ // // Deprecated.swift // RxDataSources // // Created by Krunoslav Zaher on 10/8/17. // Copyright © 2017 kzaher. All rights reserved. // #if os(iOS) || os(tvOS) extension CollectionViewSectionedDataSource { @available(*, deprecated, renamed: "configureSupplementaryView") public var supplementaryViewFactory: ConfigureSupplementaryView? { get { return self.configureSupplementaryView } set { self.configureSupplementaryView = newValue } } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/FloatingPointType+IdentifiableType.swift ================================================ // // FloatingPointType+IdentifiableType.swift // RxDataSources // // Created by Krunoslav Zaher on 7/4/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation extension FloatingPoint { typealias identity = Self public var identity: Self { return self } } extension Float : IdentifiableType { } extension Double : IdentifiableType { } ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/IntegerType+IdentifiableType.swift ================================================ // // IntegerType+IdentifiableType.swift // RxDataSources // // Created by Krunoslav Zaher on 7/4/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation extension BinaryInteger { typealias identity = Self public var identity: Self { return self } } extension Int : IdentifiableType { } extension Int8 : IdentifiableType { } extension Int16 : IdentifiableType { } extension Int32 : IdentifiableType { } extension Int64 : IdentifiableType { } extension UInt : IdentifiableType { } extension UInt8 : IdentifiableType { } extension UInt16 : IdentifiableType { } extension UInt32 : IdentifiableType { } extension UInt64 : IdentifiableType { } ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/RxCollectionViewSectionedAnimatedDataSource.swift ================================================ // // RxCollectionViewSectionedAnimatedDataSource.swift // RxExample // // Created by Krunoslav Zaher on 7/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit #if !RX_NO_MODULE import RxSwift import RxCocoa #endif import Differentiator /* This is commented becuse collection view has bugs when doing animated updates. Take a look at randomized sections. */ open class RxCollectionViewSectionedAnimatedDataSource : CollectionViewSectionedDataSource , RxCollectionViewDataSourceType { public typealias Element = [S] public typealias DecideViewTransition = (CollectionViewSectionedDataSource, UICollectionView, [Changeset]) -> ViewTransition // animation configuration public var animationConfiguration: AnimationConfiguration /// Calculates view transition depending on type of changes public var decideViewTransition: DecideViewTransition public init( animationConfiguration: AnimationConfiguration = AnimationConfiguration(), decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated }, configureCell: @escaping ConfigureCell, configureSupplementaryView: @escaping ConfigureSupplementaryView, moveItem: @escaping MoveItem = { _, _, _ in () }, canMoveItemAtIndexPath: @escaping CanMoveItemAtIndexPath = { _, _ in false } ) { self.animationConfiguration = animationConfiguration self.decideViewTransition = decideViewTransition super.init( configureCell: configureCell, configureSupplementaryView: configureSupplementaryView, moveItem: moveItem, canMoveItemAtIndexPath: canMoveItemAtIndexPath ) self.partialUpdateEvent // so in case it does produce a crash, it will be after the data has changed .observeOn(MainScheduler.asyncInstance) // Collection view has issues digesting fast updates, this should // help to alleviate the issues with them. .throttle(0.5, scheduler: MainScheduler.instance) .subscribe(onNext: { [weak self] event in self?.collectionView(event.0, throttledObservedEvent: event.1) }) .disposed(by: disposeBag) } // For some inexplicable reason, when doing animated updates first time // it crashes. Still need to figure out that one. var dataSet = false private let disposeBag = DisposeBag() // This subject and throttle are here // because collection view has problems processing animated updates fast. // This should somewhat help to alleviate the problem. private let partialUpdateEvent = PublishSubject<(UICollectionView, Event)>() /** This method exists because collection view updates are throttled because of internal collection view bugs. Collection view behaves poorly during fast updates, so this should remedy those issues. */ open func collectionView(_ collectionView: UICollectionView, throttledObservedEvent event: Event) { Binder(self) { dataSource, newSections in let oldSections = dataSource.sectionModels do { // if view is not in view hierarchy, performing batch updates will crash the app if collectionView.window == nil { dataSource.setSections(newSections) collectionView.reloadData() return } let differences = try Diff.differencesForSectionedView(initialSections: oldSections, finalSections: newSections) switch self.decideViewTransition(self, collectionView, differences) { case .animated: for difference in differences { dataSource.setSections(difference.finalSections) collectionView.performBatchUpdates(difference, animationConfiguration: self.animationConfiguration) } case .reload: self.setSections(newSections) collectionView.reloadData() } } catch let e { #if DEBUG print("Error while binding data animated: \(e)\nFallback to normal `reloadData` behavior.") rxDebugFatalError(e) #endif self.setSections(newSections) collectionView.reloadData() } }.on(event) } open func collectionView(_ collectionView: UICollectionView, observedEvent: Event) { Binder(self) { dataSource, newSections in #if DEBUG self._dataSourceBound = true #endif if !self.dataSet { self.dataSet = true dataSource.setSections(newSections) collectionView.reloadData() } else { let element = (collectionView, observedEvent) dataSource.partialUpdateEvent.on(.next(element)) } }.on(observedEvent) } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/RxCollectionViewSectionedReloadDataSource.swift ================================================ // // RxCollectionViewSectionedReloadDataSource.swift // RxExample // // Created by Krunoslav Zaher on 7/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit #if !RX_NO_MODULE import RxSwift import RxCocoa #endif import Differentiator open class RxCollectionViewSectionedReloadDataSource : CollectionViewSectionedDataSource , RxCollectionViewDataSourceType { public typealias Element = [S] open func collectionView(_ collectionView: UICollectionView, observedEvent: Event) { Binder(self) { dataSource, element in #if DEBUG self._dataSourceBound = true #endif dataSource.setSections(element) collectionView.reloadData() collectionView.collectionViewLayout.invalidateLayout() }.on(observedEvent) } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/RxPickerViewAdapter.swift ================================================ // // RxPickerViewAdapter.swift // RxDataSources // // Created by Sergey Shulga on 04/07/2017. // Copyright © 2017 kzaher. All rights reserved. // #if os(iOS) import Foundation import UIKit #if !RX_NO_MODULE import RxSwift import RxCocoa #endif /// A reactive UIPickerView adapter which uses `func pickerView(UIPickerView, titleForRow: Int, forComponent: Int)` to display the content /** Example: let adapter = RxPickerViewStringAdapter<[T]>(...) items .bind(to: firstPickerView.rx.items(adapter: adapter)) .disposed(by: disposeBag) */ open class RxPickerViewStringAdapter: RxPickerViewDataSource, UIPickerViewDelegate { /** - parameter dataSource - parameter pickerView - parameter components - parameter row - parameter component */ public typealias TitleForRow = ( _ dataSource: RxPickerViewStringAdapter, _ pickerView: UIPickerView, _ components: T, _ row: Int, _ component: Int ) -> String? private let titleForRow: TitleForRow /** - parameter components: Initial content value. - parameter numberOfComponents: Implementation of corresponding delegate method. - parameter numberOfRowsInComponent: Implementation of corresponding delegate method. - parameter titleForRow: Implementation of corresponding adapter method that converts component to `String`. */ public init(components: T, numberOfComponents: @escaping NumberOfComponents, numberOfRowsInComponent: @escaping NumberOfRowsInComponent, titleForRow: @escaping TitleForRow) { self.titleForRow = titleForRow super.init(components: components, numberOfComponents: numberOfComponents, numberOfRowsInComponent: numberOfRowsInComponent) } open func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return titleForRow(self, pickerView, components, row, component) } } /// A reactive UIPickerView adapter which uses `func pickerView(UIPickerView, viewForRow: Int, forComponent: Int, reusing: UIView?)` to display the content /** Example: let adapter = RxPickerViewAttributedStringAdapter<[T]>(...) items .bind(to: firstPickerView.rx.items(adapter: adapter)) .disposed(by: disposeBag) */ open class RxPickerViewAttributedStringAdapter: RxPickerViewDataSource, UIPickerViewDelegate { /** - parameter dataSource - parameter pickerView - parameter components - parameter row - parameter component */ public typealias AttributedTitleForRow = ( _ dataSource: RxPickerViewAttributedStringAdapter, _ pickerView: UIPickerView, _ components: T, _ row: Int, _ component: Int ) -> NSAttributedString? private let attributedTitleForRow: AttributedTitleForRow /** - parameter components: Initial content value. - parameter numberOfComponents: Implementation of corresponding delegate method. - parameter numberOfRowsInComponent: Implementation of corresponding delegate method. - parameter attributedTitleForRow: Implementation of corresponding adapter method that converts component to `NSAttributedString`. */ public init(components: T, numberOfComponents: @escaping NumberOfComponents, numberOfRowsInComponent: @escaping NumberOfRowsInComponent, attributedTitleForRow: @escaping AttributedTitleForRow) { self.attributedTitleForRow = attributedTitleForRow super.init(components: components, numberOfComponents: numberOfComponents, numberOfRowsInComponent: numberOfRowsInComponent) } open func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? { return attributedTitleForRow(self, pickerView, components, row, component) } } /// A reactive UIPickerView adapter which uses `func pickerView(pickerView:, viewForRow row:, forComponent component:)` to display the content /** Example: let adapter = RxPickerViewViewAdapter<[T]>(...) items .bind(to: firstPickerView.rx.items(adapter: adapter)) .disposed(by: disposeBag) */ open class RxPickerViewViewAdapter: RxPickerViewDataSource, UIPickerViewDelegate { /** - parameter dataSource - parameter pickerView - parameter components - parameter row - parameter component - parameter view */ public typealias ViewForRow = ( _ dataSource: RxPickerViewViewAdapter, _ pickerView: UIPickerView, _ components: T, _ row: Int, _ component: Int, _ view: UIView? ) -> UIView private let viewForRow: ViewForRow /** - parameter components: Initial content value. - parameter numberOfComponents: Implementation of corresponding delegate method. - parameter numberOfRowsInComponent: Implementation of corresponding delegate method. - parameter attributedTitleForRow: Implementation of corresponding adapter method that converts component to `UIView`. */ public init(components: T, numberOfComponents: @escaping NumberOfComponents, numberOfRowsInComponent: @escaping NumberOfRowsInComponent, viewForRow: @escaping ViewForRow) { self.viewForRow = viewForRow super.init(components: components, numberOfComponents: numberOfComponents, numberOfRowsInComponent: numberOfRowsInComponent) } open func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { return viewForRow(self, pickerView, components, row, component, view) } } /// A reactive UIPickerView data source open class RxPickerViewDataSource: NSObject, UIPickerViewDataSource { /** - parameter dataSource - parameter pickerView - parameter components */ public typealias NumberOfComponents = ( _ dataSource: RxPickerViewDataSource, _ pickerView: UIPickerView, _ components: T) -> Int /** - parameter dataSource - parameter pickerView - parameter components - parameter component */ public typealias NumberOfRowsInComponent = ( _ dataSource: RxPickerViewDataSource, _ pickerView: UIPickerView, _ components: T, _ component: Int ) -> Int fileprivate var components: T /** - parameter components: Initial content value. - parameter numberOfComponents: Implementation of corresponding delegate method. - parameter numberOfRowsInComponent: Implementation of corresponding delegate method. */ init(components: T, numberOfComponents: @escaping NumberOfComponents, numberOfRowsInComponent: @escaping NumberOfRowsInComponent) { self.components = components self.numberOfComponents = numberOfComponents self.numberOfRowsInComponent = numberOfRowsInComponent super.init() } private let numberOfComponents: NumberOfComponents private let numberOfRowsInComponent: NumberOfRowsInComponent //MARK: UIPickerViewDataSource public func numberOfComponents(in pickerView: UIPickerView) -> Int { return numberOfComponents(self, pickerView, components) } public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return numberOfRowsInComponent(self, pickerView, components, component) } } extension RxPickerViewDataSource: RxPickerViewDataSourceType { public func pickerView(_ pickerView: UIPickerView, observedEvent: Event) { Binder(self) { (dataSource, components) in dataSource.components = components pickerView.reloadAllComponents() }.on(observedEvent) } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/RxTableViewSectionedAnimatedDataSource.swift ================================================ // // RxTableViewSectionedAnimatedDataSource.swift // RxExample // // Created by Krunoslav Zaher on 6/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit #if !RX_NO_MODULE import RxSwift import RxCocoa #endif import Differentiator open class RxTableViewSectionedAnimatedDataSource : TableViewSectionedDataSource , RxTableViewDataSourceType { public typealias Element = [S] public typealias DecideViewTransition = (TableViewSectionedDataSource, UITableView, [Changeset]) -> ViewTransition /// Animation configuration for data source public var animationConfiguration: AnimationConfiguration /// Calculates view transition depending on type of changes public var decideViewTransition: DecideViewTransition #if os(iOS) public init( animationConfiguration: AnimationConfiguration = AnimationConfiguration(), decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated }, configureCell: @escaping ConfigureCell, titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil }, titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil }, canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false }, canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false }, sectionIndexTitles: @escaping SectionIndexTitles = { _ in nil }, sectionForSectionIndexTitle: @escaping SectionForSectionIndexTitle = { _, _, index in index } ) { self.animationConfiguration = animationConfiguration self.decideViewTransition = decideViewTransition super.init( configureCell: configureCell, titleForHeaderInSection: titleForHeaderInSection, titleForFooterInSection: titleForFooterInSection, canEditRowAtIndexPath: canEditRowAtIndexPath, canMoveRowAtIndexPath: canMoveRowAtIndexPath, sectionIndexTitles: sectionIndexTitles, sectionForSectionIndexTitle: sectionForSectionIndexTitle ) } #else public init( animationConfiguration: AnimationConfiguration = AnimationConfiguration(), decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated }, configureCell: @escaping ConfigureCell, titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil }, titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil }, canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false }, canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false } ) { self.animationConfiguration = animationConfiguration self.decideViewTransition = decideViewTransition super.init( configureCell: configureCell, titleForHeaderInSection: titleForHeaderInSection, titleForFooterInSection: titleForFooterInSection, canEditRowAtIndexPath: canEditRowAtIndexPath, canMoveRowAtIndexPath: canMoveRowAtIndexPath ) } #endif var dataSet = false open func tableView(_ tableView: UITableView, observedEvent: Event) { Binder(self) { dataSource, newSections in #if DEBUG self._dataSourceBound = true #endif if !self.dataSet { self.dataSet = true dataSource.setSections(newSections) tableView.reloadData() } else { DispatchQueue.main.async { // if view is not in view hierarchy, performing batch updates will crash the app if tableView.window == nil { dataSource.setSections(newSections) tableView.reloadData() return } let oldSections = dataSource.sectionModels do { let differences = try Diff.differencesForSectionedView(initialSections: oldSections, finalSections: newSections) switch self.decideViewTransition(self, tableView, differences) { case .animated: for difference in differences { dataSource.setSections(difference.finalSections) tableView.performBatchUpdates(difference, animationConfiguration: self.animationConfiguration) } case .reload: self.setSections(newSections) tableView.reloadData() return } } catch let e { rxDebugFatalError(e) self.setSections(newSections) tableView.reloadData() } } } }.on(observedEvent) } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/RxTableViewSectionedReloadDataSource.swift ================================================ // // RxTableViewSectionedReloadDataSource.swift // RxExample // // Created by Krunoslav Zaher on 6/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit #if !RX_NO_MODULE import RxSwift import RxCocoa #endif import Differentiator open class RxTableViewSectionedReloadDataSource : TableViewSectionedDataSource , RxTableViewDataSourceType { public typealias Element = [S] open func tableView(_ tableView: UITableView, observedEvent: Event) { Binder(self) { dataSource, element in #if DEBUG self._dataSourceBound = true #endif dataSource.setSections(element) tableView.reloadData() }.on(observedEvent) } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/String+IdentifiableType.swift ================================================ // // String+IdentifiableType.swift // RxDataSources // // Created by Krunoslav Zaher on 7/4/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Foundation extension String : IdentifiableType { public typealias Identity = String public var identity: String { return self } } ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/TableViewSectionedDataSource.swift ================================================ // // TableViewSectionedDataSource.swift // RxDataSources // // Created by Krunoslav Zaher on 6/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit #if !RX_NO_MODULE import RxCocoa #endif import Differentiator open class TableViewSectionedDataSource : NSObject , UITableViewDataSource , SectionedViewDataSourceType { public typealias I = S.Item public typealias Section = S public typealias ConfigureCell = (TableViewSectionedDataSource, UITableView, IndexPath, I) -> UITableViewCell public typealias TitleForHeaderInSection = (TableViewSectionedDataSource, Int) -> String? public typealias TitleForFooterInSection = (TableViewSectionedDataSource, Int) -> String? public typealias CanEditRowAtIndexPath = (TableViewSectionedDataSource, IndexPath) -> Bool public typealias CanMoveRowAtIndexPath = (TableViewSectionedDataSource, IndexPath) -> Bool #if os(iOS) public typealias SectionIndexTitles = (TableViewSectionedDataSource) -> [String]? public typealias SectionForSectionIndexTitle = (TableViewSectionedDataSource, _ title: String, _ index: Int) -> Int #endif #if os(iOS) public init( configureCell: @escaping ConfigureCell, titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil }, titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil }, canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false }, canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false }, sectionIndexTitles: @escaping SectionIndexTitles = { _ in nil }, sectionForSectionIndexTitle: @escaping SectionForSectionIndexTitle = { _, _, index in index } ) { self.configureCell = configureCell self.titleForHeaderInSection = titleForHeaderInSection self.titleForFooterInSection = titleForFooterInSection self.canEditRowAtIndexPath = canEditRowAtIndexPath self.canMoveRowAtIndexPath = canMoveRowAtIndexPath self.sectionIndexTitles = sectionIndexTitles self.sectionForSectionIndexTitle = sectionForSectionIndexTitle } #else public init( configureCell: @escaping ConfigureCell, titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil }, titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil }, canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false }, canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false } ) { self.configureCell = configureCell self.titleForHeaderInSection = titleForHeaderInSection self.titleForFooterInSection = titleForFooterInSection self.canEditRowAtIndexPath = canEditRowAtIndexPath self.canMoveRowAtIndexPath = canMoveRowAtIndexPath } #endif #if DEBUG // If data source has already been bound, then mutating it // afterwards isn't something desired. // This simulates immutability after binding var _dataSourceBound: Bool = false private func ensureNotMutatedAfterBinding() { assert(!_dataSourceBound, "Data source is already bound. Please write this line before binding call (`bindTo`, `drive`). Data source must first be completely configured, and then bound after that, otherwise there could be runtime bugs, glitches, or partial malfunctions.") } #endif // This structure exists because model can be mutable // In that case current state value should be preserved. // The state that needs to be preserved is ordering of items in section // and their relationship with section. // If particular item is mutable, that is irrelevant for this logic to function // properly. public typealias SectionModelSnapshot = SectionModel private var _sectionModels: [SectionModelSnapshot] = [] open var sectionModels: [S] { return _sectionModels.map { Section(original: $0.model, items: $0.items) } } open subscript(section: Int) -> S { let sectionModel = self._sectionModels[section] return S(original: sectionModel.model, items: sectionModel.items) } open subscript(indexPath: IndexPath) -> I { get { return self._sectionModels[indexPath.section].items[indexPath.item] } set(item) { var section = self._sectionModels[indexPath.section] section.items[indexPath.item] = item self._sectionModels[indexPath.section] = section } } open func model(at indexPath: IndexPath) throws -> Any { return self[indexPath] } open func setSections(_ sections: [S]) { self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) } } open var configureCell: ConfigureCell { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var titleForHeaderInSection: TitleForHeaderInSection { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var titleForFooterInSection: TitleForFooterInSection { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var canEditRowAtIndexPath: CanEditRowAtIndexPath { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var canMoveRowAtIndexPath: CanMoveRowAtIndexPath { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var rowAnimation: UITableViewRowAnimation = .automatic #if os(iOS) open var sectionIndexTitles: SectionIndexTitles { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } open var sectionForSectionIndexTitle: SectionForSectionIndexTitle { didSet { #if DEBUG ensureNotMutatedAfterBinding() #endif } } #endif // UITableViewDataSource open func numberOfSections(in tableView: UITableView) -> Int { return _sectionModels.count } open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard _sectionModels.count > section else { return 0 } return _sectionModels[section].items.count } open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { precondition(indexPath.item < _sectionModels[indexPath.section].items.count) return configureCell(self, tableView, indexPath, self[indexPath]) } open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return titleForHeaderInSection(self, section) } open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { return titleForFooterInSection(self, section) } open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return canEditRowAtIndexPath(self, indexPath) } open func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { return canMoveRowAtIndexPath(self, indexPath) } open func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath) } #if os(iOS) open func sectionIndexTitles(for tableView: UITableView) -> [String]? { return sectionIndexTitles(self) } open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { return sectionForSectionIndexTitle(self, title, index) } #endif } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/UI+SectionedViewType.swift ================================================ // // UI+SectionedViewType.swift // RxDataSources // // Created by Krunoslav Zaher on 6/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import Foundation import UIKit import Differentiator func indexSet(_ values: [Int]) -> IndexSet { let indexSet = NSMutableIndexSet() for i in values { indexSet.add(i) } return indexSet as IndexSet } extension UITableView : SectionedViewType { public func insertItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) { self.insertRows(at: paths, with: animationStyle) } public func deleteItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) { self.deleteRows(at: paths, with: animationStyle) } public func moveItemAtIndexPath(_ from: IndexPath, to: IndexPath) { self.moveRow(at: from, to: to) } public func reloadItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) { self.reloadRows(at: paths, with: animationStyle) } public func insertSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) { self.insertSections(indexSet(sections), with: animationStyle) } public func deleteSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) { self.deleteSections(indexSet(sections), with: animationStyle) } public func moveSection(_ from: Int, to: Int) { self.moveSection(from, toSection: to) } public func reloadSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) { self.reloadSections(indexSet(sections), with: animationStyle) } public func performBatchUpdates(_ changes: Changeset, animationConfiguration: AnimationConfiguration) { self.beginUpdates() _performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration) self.endUpdates() } } extension UICollectionView : SectionedViewType { public func insertItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) { self.insertItems(at: paths) } public func deleteItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) { self.deleteItems(at: paths) } public func moveItemAtIndexPath(_ from: IndexPath, to: IndexPath) { self.moveItem(at: from, to: to) } public func reloadItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) { self.reloadItems(at: paths) } public func insertSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) { self.insertSections(indexSet(sections)) } public func deleteSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) { self.deleteSections(indexSet(sections)) } public func moveSection(_ from: Int, to: Int) { self.moveSection(from, toSection: to) } public func reloadSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) { self.reloadSections(indexSet(sections)) } public func performBatchUpdates(_ changes: Changeset, animationConfiguration: AnimationConfiguration) { self.performBatchUpdates({ () -> Void in _performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration) }, completion: { (completed: Bool) -> Void in }) } } public protocol SectionedViewType { func insertItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) func deleteItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) func moveItemAtIndexPath(_ from: IndexPath, to: IndexPath) func reloadItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) func insertSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) func deleteSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) func moveSection(_ from: Int, to: Int) func reloadSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) func performBatchUpdates(_ changes: Changeset, animationConfiguration: AnimationConfiguration) } func _performBatchUpdates(_ view: V, changes: Changeset, animationConfiguration:AnimationConfiguration) { typealias I = S.Item view.deleteSections(changes.deletedSections, animationStyle: animationConfiguration.deleteAnimation) // Updated sections doesn't mean reload entire section, somebody needs to update the section view manually // otherwise all cells will be reloaded for nothing. //view.reloadSections(changes.updatedSections, animationStyle: rowAnimation) view.insertSections(changes.insertedSections, animationStyle: animationConfiguration.insertAnimation) for (from, to) in changes.movedSections { view.moveSection(from, to: to) } view.deleteItemsAtIndexPaths( changes.deletedItems.map { IndexPath(item: $0.itemIndex, section: $0.sectionIndex) }, animationStyle: animationConfiguration.deleteAnimation ) view.insertItemsAtIndexPaths( changes.insertedItems.map { IndexPath(item: $0.itemIndex, section: $0.sectionIndex) }, animationStyle: animationConfiguration.insertAnimation ) view.reloadItemsAtIndexPaths( changes.updatedItems.map { IndexPath(item: $0.itemIndex, section: $0.sectionIndex) }, animationStyle: animationConfiguration.reloadAnimation ) for (from, to) in changes.movedItems { view.moveItemAtIndexPath( IndexPath(item: from.itemIndex, section: from.sectionIndex), to: IndexPath(item: to.itemIndex, section: to.sectionIndex) ) } } #endif ================================================ FILE: Pods/RxDataSources/Sources/RxDataSources/ViewTransition.swift ================================================ // // ViewTransition.swift // RxDataSources // // Created by Krunoslav Zaher on 10/22/17. // Copyright © 2017 kzaher. All rights reserved. // /// Transition between two view states public enum ViewTransition { /// animated transition case animated /// refresh view without animations case reload } ================================================ FILE: Pods/RxGesture/LICENSE ================================================ Copyright (c) RxSwiftCommunity 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. ================================================ FILE: Pods/RxGesture/Pod/Classes/GestureFactory.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import RxSwift import RxCocoa import ObjectiveC public protocol GestureRecognizerFactory { associatedtype Gesture: GestureRecognizer var configuration: (Gesture, RxGestureRecognizerDelegate) -> Void { get } func make() -> Gesture } private var gestureRecognizerStrongDelegateKey: UInt8 = 0 public extension GestureRecognizerFactory { public var configuration: (Gesture, RxGestureRecognizerDelegate) -> Void { return { _, _ in } } public func make() -> Gesture { let gesture = Gesture() let delegate = RxGestureRecognizerDelegate() objc_setAssociatedObject( gesture, &gestureRecognizerStrongDelegateKey, delegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) gesture.delegate = delegate configuration(gesture, delegate) return gesture } } public struct AnyGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = GestureRecognizer public init(_ factory: G) { _make = { return factory.make() as GestureRecognizer } } private let _make: () -> GestureRecognizer public func make() -> GestureRecognizer { return _make() } } ================================================ FILE: Pods/RxGesture/Pod/Classes/RxGestureRecognizerDelegate.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. #if os(iOS) import UIKit #elseif os(OSX) import AppKit #endif import RxSwift import RxCocoa public struct GestureRecognizerDelegatePolicy { public typealias PolicyBody = (PolicyInput) -> Bool private let policy: PolicyBody private init(policy: @escaping PolicyBody) { self.policy = policy } public static func custom(_ policy: @escaping PolicyBody) -> GestureRecognizerDelegatePolicy { return .init(policy: policy) } public static var always: GestureRecognizerDelegatePolicy { return .init { _ in true } } public static var never: GestureRecognizerDelegatePolicy { return .init { _ in false } } fileprivate func isPolicyPassing(with args: PolicyInput) -> Bool { return policy(args) } } public final class RxGestureRecognizerDelegate: NSObject, GestureRecognizerDelegate { public var beginPolicy: GestureRecognizerDelegatePolicy = .always public var touchReceptionPolicy: GestureRecognizerDelegatePolicy<(GestureRecognizer, Touch)> = .always public var selfFailureRequirementPolicy: GestureRecognizerDelegatePolicy<(GestureRecognizer, GestureRecognizer)> = .never public var otherFailureRequirementPolicy: GestureRecognizerDelegatePolicy<(GestureRecognizer, GestureRecognizer)> = .never public var simultaneousRecognitionPolicy: GestureRecognizerDelegatePolicy<(GestureRecognizer, GestureRecognizer)> = .always #if os(iOS) // Workaround because we can't have stored properties with @available annotation private var _pressReceptionPolicy: Any? @available(iOS 9.0, *) public var pressReceptionPolicy: GestureRecognizerDelegatePolicy<(GestureRecognizer, UIPress)> { get { if let policy = _pressReceptionPolicy as? GestureRecognizerDelegatePolicy<(GestureRecognizer, UIPress)> { return policy } return GestureRecognizerDelegatePolicy<(GestureRecognizer, UIPress)>.always } set { _pressReceptionPolicy = newValue } } #endif #if os(OSX) public var eventRecognitionAttemptPolicy: GestureRecognizerDelegatePolicy<(GestureRecognizer, NSEvent)> = .always #endif public func gestureRecognizerShouldBegin( _ gestureRecognizer: GestureRecognizer ) -> Bool { return beginPolicy.isPolicyPassing(with: gestureRecognizer) } public func gestureRecognizer( _ gestureRecognizer: GestureRecognizer, shouldReceive touch: Touch ) -> Bool { return touchReceptionPolicy.isPolicyPassing( with: (gestureRecognizer, touch) ) } public func gestureRecognizer( _ gestureRecognizer: GestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: GestureRecognizer ) -> Bool { return otherFailureRequirementPolicy.isPolicyPassing( with: (gestureRecognizer, otherGestureRecognizer) ) } public func gestureRecognizer( _ gestureRecognizer: GestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: GestureRecognizer ) -> Bool { return selfFailureRequirementPolicy.isPolicyPassing( with: (gestureRecognizer, otherGestureRecognizer) ) } public func gestureRecognizer( _ gestureRecognizer: GestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: GestureRecognizer ) -> Bool { return simultaneousRecognitionPolicy.isPolicyPassing( with: (gestureRecognizer, otherGestureRecognizer) ) } #if os(iOS) @available(iOS 9.0, *) public func gestureRecognizer( _ gestureRecognizer: UIGestureRecognizer, shouldReceive press: UIPress ) -> Bool { return pressReceptionPolicy.isPolicyPassing( with: (gestureRecognizer, press) ) } #endif #if os(OSX) public func gestureRecognizer( _ gestureRecognizer: NSGestureRecognizer, shouldAttemptToRecognizeWith event: NSEvent ) -> Bool { return eventRecognitionAttemptPolicy.isPolicyPassing( with: (gestureRecognizer, event) ) } #endif } ================================================ FILE: Pods/RxGesture/Pod/Classes/SharedTypes.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import Foundation #if os(iOS) import UIKit public typealias Touch = UITouch public typealias GestureRecognizer = UIGestureRecognizer public typealias GestureRecognizerState = UIGestureRecognizerState public typealias GestureRecognizerDelegate = UIGestureRecognizerDelegate public typealias View = UIView #elseif os(OSX) import AppKit public typealias Touch = NSTouch public typealias GestureRecognizer = NSGestureRecognizer public typealias GestureRecognizerState = NSGestureRecognizer.State public typealias GestureRecognizerDelegate = NSGestureRecognizerDelegate public typealias View = NSView #endif public enum TargetView { /// The target view will be the gestureRecognizer's view case view /// The target view will be the gestureRecognizer's view's superview case superview /// The target view will be the gestureRecognizer's view's window case window /// The target view will be the given view case this(View) public func targetView(for gestureRecognizer: GestureRecognizer) -> View? { switch self { case .view: return gestureRecognizer.view case .superview: return gestureRecognizer.view?.superview case .window: #if os(iOS) return gestureRecognizer.view?.window #elseif os(OSX) return gestureRecognizer.view?.window?.contentView #endif case .this(let view): return view } } } ================================================ FILE: Pods/RxGesture/Pod/Classes/View+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import RxSwift import RxCocoa extension Reactive where Base: View { /** Reactive wrapper for multiple view gesture recognizers. It automatically attaches the gesture recognizers to the receiver view. The value the `Observable` emits is the gesture recognizer itself. rx.anyGesture can't error and is subscribed/observed on main scheduler. - parameter factories: a `(GestureRecognizerFactory + state)` collection you want to use to create the `GestureRecognizers` to add and observe - returns: a `ControlEvent` that re-emit the gesture recognizer itself */ public func anyGesture(_ factories: (AnyGestureRecognizerFactory, when: GestureRecognizerState)...) -> ControlEvent { let observables = factories.map { gesture, state in self.gesture(gesture).when(state).asObservable() as Observable } let source = Observable.from(observables).merge() return ControlEvent(events: source) } /** Reactive wrapper for multiple view gesture recognizers. It automatically attaches the gesture recognizers to the receiver view. The value the `Observable` emits is the gesture recognizer itself. rx.anyGesture can't error and is subscribed/observed on main scheduler. - parameter factories: a `GestureRecognizerFactory` collection you want to use to create the `GestureRecognizers` to add and observe - returns: a `ControlEvent` that re-emit the gesture recognizer itself */ public func anyGesture(_ factories: AnyGestureRecognizerFactory...) -> ControlEvent { let observables = factories.map { gesture in self.gesture(gesture).asObservable() as Observable } let source = Observable.from(observables).merge() return ControlEvent(events: source) } /** Reactive wrapper for a single view gesture recognizer. It automatically attaches the gesture recognizer to the receiver view. The value the `Observable` emits is the gesture recognizer itself. rx.gesture can't error and is subscribed/observed on main scheduler. - parameter factory: a `GestureRecognizerFactory` you want to use to create the `GestureRecognizer` to add and observe - returns: a `ControlEvent` that re-emit the gesture recognizer itself */ public func gesture(_ factory: GF) -> ControlEvent where GF.Gesture == G { return self.gesture(factory.make()) } /** Reactive wrapper for a single view gesture recognizer. It automatically attaches the gesture recognizer to the receiver view. The value the `Observable` emits is the gesture recognizer itself. rx.gesture can't error and is subscribed/observed on main scheduler. - parameter gesture: a `GestureRecognizer` you want to add and observe - returns: a `ControlEvent` that re-emit the gesture recognizer itself */ public func gesture(_ gesture: G) -> ControlEvent { let source = Observable.deferred { [weak control = self.base] () -> Observable in MainScheduler.ensureExecutingOnScheduler() guard let control = control else { return .empty() } let genericGesture = gesture as GestureRecognizer #if os(iOS) control.isUserInteractionEnabled = true #endif control.addGestureRecognizer(gesture) return genericGesture.rx.event .map { $0 as! G } .startWith(gesture) .do(onDispose: { [weak control, weak gesture] () in guard let gesture = gesture else { return } control?.removeGestureRecognizer(gesture) }) .takeUntil(control.rx.deallocated) } return ControlEvent(events: source) } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/TransformGestureRecognizers.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa public protocol TransformGestureRecognizersType { var panGesture: UIPanGestureRecognizer { get } var rotationGesture: UIRotationGestureRecognizer { get } var pinchGesture: UIPinchGestureRecognizer { get } } public struct TransformGestureRecognizers: TransformGestureRecognizersType { public let panGesture: UIPanGestureRecognizer public let rotationGesture: UIRotationGestureRecognizer public let pinchGesture: UIPinchGestureRecognizer } public struct TransformVelocity { let translation: CGPoint let rotation: CGFloat let scale: CGFloat } public extension Reactive where Base: UIView { public func transformGestures( configuration: ((TransformGestureRecognizers, RxGestureRecognizerDelegate) -> Void)? = nil ) -> ControlEvent { let source = Observable.combineLatest(panGesture(), rotationGesture(), pinchGesture()) { return TransformGestureRecognizers( panGesture: $0, rotationGesture: $1, pinchGesture: $2 ) } return ControlEvent(events: source) } } public extension ObservableType where E: TransformGestureRecognizersType { public func when(_ states: UIGestureRecognizerState...) -> Observable { return filter { gestures in return states.contains(gestures.panGesture.state) || states.contains(gestures.rotationGesture.state) || states.contains(gestures.pinchGesture.state) } } public func asTransform(in view: TargetView = .view) -> Observable<(transform: CGAffineTransform, velocity: TransformVelocity)> { return self.map { gestures in let translationView = view.targetView(for: gestures.panGesture) let translation = gestures.panGesture.translation(in: translationView) let transform = CGAffineTransform.identity .rotated(by: gestures.rotationGesture.rotation) .scaledBy(x: gestures.pinchGesture.scale, y: gestures.pinchGesture.scale) .translatedBy(x: translation.x, y: translation.y) let velocity = TransformVelocity( translation: gestures.panGesture.velocity(in: translationView), rotation: gestures.rotationGesture.velocity, scale: gestures.pinchGesture.velocity ) return (transform, velocity) } } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UIGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa public extension ObservableType where E: UIGestureRecognizer { /** Filters the observable `GestureRecognizer` events sequence based on the `GestureRecognizer` state. - parameter state: An `UIGestureRecognizerState` that is used to filter the `GestureRecognizer` events sequence. - returns: An observable `GestureRecognizer` events sequence that only contains events emitted while the `GestureRecognizer`'s state match the given `state`. */ public func when(_ states: UIGestureRecognizerState...) -> Observable { return filter { gesture in return states.contains(gesture.state) } } /** Filters the observable `GestureRecognizer` events sequence based on the `GestureRecognizer` state. - parameter state: An `UIGestureRecognizerState` that is used to filter the `GestureRecognizer` events sequence. - returns: An observable `GestureRecognizer` events sequence that only contains events emitted while the `GestureRecognizer`'s state match the given `state`. */ internal func when(_ states: [UIGestureRecognizerState]) -> Observable { return filter { gesture in return states.contains(gesture.state) } } /** Maps the observable `GestureRecognizer` events sequence to an observable sequence of points computed as the location in the given `view` of the gesture. - parameter view: A `TargetView` value on which the gesture took place. */ public func asLocation(in view: TargetView = .view) -> Observable { return map { gesture in return gesture.location(in: view.targetView(for: gesture)) } } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UILongPressGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa /// Default values for `UILongPressGestureRecognizer` configuration public enum LongPressGestureRecognizerDefaults { public static var numberOfTouchesRequired: Int = 1 public static var numberOfTapsRequired: Int = 0 public static var minimumPressDuration: CFTimeInterval = 0.5 public static var allowableMovement: CGFloat = 10 public static var configuration: ((UILongPressGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? } fileprivate typealias Defaults = LongPressGestureRecognizerDefaults /// A `GestureRecognizerFactory` for `UITapGestureRecognizer` public struct LongPressGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = UILongPressGestureRecognizer public let configuration: (UILongPressGestureRecognizer, RxGestureRecognizerDelegate) -> Void /** Initialiaze a `GestureRecognizerFactory` for `UILongPressGestureRecognizer` - parameter numberOfTouchesRequired: Number of fingers that must be held down for the gesture to be recognized - parameter numberOfTapsRequired: The number of full taps required before the press for gesture to be recognized - parameter minimumPressDuration: Time in seconds the fingers must be held down for the gesture to be recognized - parameter allowableMovement: Maximum movement in pixels allowed before the gesture fails. Once recognized (after minimumPressDuration) there is no limit on finger movement for the remainder of the touch tracking - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public init( numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, numberOfTapsRequired: Int = Defaults.numberOfTapsRequired, minimumPressDuration: CFTimeInterval = Defaults.minimumPressDuration, allowableMovement: CGFloat = Defaults.allowableMovement, configuration: ((UILongPressGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) { self.configuration = { gestureRecognizer, delegate in gestureRecognizer.numberOfTouchesRequired = numberOfTouchesRequired gestureRecognizer.numberOfTapsRequired = numberOfTapsRequired gestureRecognizer.minimumPressDuration = minimumPressDuration gestureRecognizer.allowableMovement = allowableMovement configuration?(gestureRecognizer, delegate) } } } extension AnyGestureRecognizerFactory { /** Returns an `AnyGestureRecognizerFactory` for `UILongPressGestureRecognizer` - parameter numberOfTouchesRequired: Number of fingers that must be held down for the gesture to be recognized - parameter numberOfTapsRequired: The number of full taps required before the press for gesture to be recognized - parameter minimumPressDuration: Time in seconds the fingers must be held down for the gesture to be recognized - parameter allowableMovement: Maximum movement in pixels allowed before the gesture fails. Once recognized (after minimumPressDuration) there is no limit on finger movement for the remainder of the touch tracking - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public static func longPress( numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, numberOfTapsRequired: Int = Defaults.numberOfTapsRequired, minimumPressDuration: CFTimeInterval = Defaults.minimumPressDuration, allowableMovement: CGFloat = Defaults.allowableMovement, configuration: ((UILongPressGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> AnyGestureRecognizerFactory { let gesture = LongPressGestureRecognizerFactory( numberOfTouchesRequired: numberOfTouchesRequired, numberOfTapsRequired: numberOfTapsRequired, minimumPressDuration: minimumPressDuration, allowableMovement: allowableMovement, configuration: configuration ) return AnyGestureRecognizerFactory(gesture) } } public extension Reactive where Base: UIView { /** Returns an observable `UILongPressGestureRecognizer` events sequence - parameter numberOfTouchesRequired: Number of fingers that must be held down for the gesture to be recognized - parameter numberOfTapsRequired: The number of full taps required before the press for gesture to be recognized - parameter minimumPressDuration: Time in seconds the fingers must be held down for the gesture to be recognized - parameter allowableMovement: Maximum movement in pixels allowed before the gesture fails. Once recognized (after minimumPressDuration) there is no limit on finger movement for the remainder of the touch tracking - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public func longPressGesture( numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, numberOfTapsRequired: Int = Defaults.numberOfTapsRequired, minimumPressDuration: CFTimeInterval = Defaults.minimumPressDuration, allowableMovement: CGFloat = Defaults.allowableMovement, configuration: ((UILongPressGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> ControlEvent { return gesture(LongPressGestureRecognizerFactory( numberOfTouchesRequired: numberOfTouchesRequired, numberOfTapsRequired: numberOfTapsRequired, minimumPressDuration: minimumPressDuration, allowableMovement: allowableMovement, configuration: configuration )) } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UIPanGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa /// Default values for `UIPanGestureRecognizer` configuration public enum PanGestureRecognizerDefaults { public static var minimumNumberOfTouches: Int = 1 public static var maximumNumberOfTouches: Int = Int.max public static var configuration: ((UIPanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? } fileprivate typealias Defaults = PanGestureRecognizerDefaults /// A `GestureRecognizerFactory` for `UIPanGestureRecognizer` public struct PanGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = UIPanGestureRecognizer public let configuration: (UIPanGestureRecognizer, RxGestureRecognizerDelegate) -> Void /** Initialiaze a `GestureRecognizerFactory` for `UITapGestureRecognizer` - parameter minimumNumberOfTouches: The minimum number of touches required to match - parameter maximumNumberOfTouches: The maximum number of touches that can be down - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public init( minimumNumberOfTouches: Int = Defaults.minimumNumberOfTouches, maximumNumberOfTouches: Int = Defaults.maximumNumberOfTouches, configuration: ((UIPanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) { self.configuration = { gesture, delegate in gesture.minimumNumberOfTouches = minimumNumberOfTouches gesture.maximumNumberOfTouches = maximumNumberOfTouches configuration?(gesture, delegate) } } } extension AnyGestureRecognizerFactory { /** Returns an `AnyGestureRecognizerFactory` for `UIPanGestureRecognizer` - parameter minimumNumberOfTouches: The minimum number of touches required to match - parameter maximumNumberOfTouches: The maximum number of touches that can be down - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public static func pan( minimumNumberOfTouches: Int = Defaults.minimumNumberOfTouches, maximumNumberOfTouches: Int = Defaults.maximumNumberOfTouches, configuration: ((UIPanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> AnyGestureRecognizerFactory { let gesture = PanGestureRecognizerFactory( minimumNumberOfTouches: minimumNumberOfTouches, maximumNumberOfTouches: maximumNumberOfTouches, configuration: configuration ) return AnyGestureRecognizerFactory(gesture) } } public extension Reactive where Base: UIView { /** Returns an observable `UIPanGestureRecognizer` events sequence - parameter minimumNumberOfTouches: The minimum number of touches required to match - parameter maximumNumberOfTouches: The maximum number of touches that can be down - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public func panGesture( minimumNumberOfTouches: Int = Defaults.minimumNumberOfTouches, maximumNumberOfTouches: Int = Defaults.maximumNumberOfTouches, configuration: ((UIPanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> ControlEvent { return gesture(PanGestureRecognizerFactory( minimumNumberOfTouches: minimumNumberOfTouches, maximumNumberOfTouches: maximumNumberOfTouches, configuration: configuration )) } } public extension ObservableType where E: UIPanGestureRecognizer { /** Maps the observable `GestureRecognizer` events sequence to an observable sequence of translation values of the pan gesture in the coordinate system of the specified `view` alongside the gesture velocity. - parameter view: A `TargetView` value on which the gesture took place. */ public func asTranslation(in view: TargetView = .view) -> Observable<(translation: CGPoint, velocity: CGPoint)> { return self.map { gesture in let view = view.targetView(for: gesture) return ( gesture.translation(in: view), gesture.velocity(in: view) ) } } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UIPinchGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa /// Default values for `UIPinchGestureRecognizer` configuration public enum PinchGestureRecognizerDefaults { public static var configuration: ((UIPinchGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? } fileprivate typealias Defaults = PinchGestureRecognizerDefaults /// A `GestureRecognizerFactory` for `UIPinchGestureRecognizer` public struct PinchGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = UIPinchGestureRecognizer public let configuration: (UIPinchGestureRecognizer, RxGestureRecognizerDelegate) -> Void /** Initialiaze a `GestureRecognizerFactory` for `UIPinchGestureRecognizer` - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public init( configuration: ((UIPinchGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) { self.configuration = configuration ?? { _, _ in } } } extension AnyGestureRecognizerFactory { /** Returns an `AnyGestureRecognizerFactory` for `UIPinchGestureRecognizer` - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public static func pinch( configuration: ((UIPinchGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> AnyGestureRecognizerFactory { let gesture = PinchGestureRecognizerFactory( configuration: configuration ) return AnyGestureRecognizerFactory(gesture) } } public extension Reactive where Base: UIView { /** Returns an observable `UIPinchGestureRecognizer` events sequence - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public func pinchGesture( configuration: ((UIPinchGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> ControlEvent { return gesture(PinchGestureRecognizerFactory( configuration: configuration )) } } public extension ObservableType where E: UIPinchGestureRecognizer { /** Maps the observable `GestureRecognizer` events sequence to an observable sequence of scale factors relative to the points of the two touches in screen coordinates alongside the gesture velocity. */ public func asScale() -> Observable<(scale: CGFloat, velocity: CGFloat)> { return self.map { gesture in return (gesture.scale, gesture.velocity) } } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UIRotationGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa /// Default values for `UIRotationGestureRecognizer` configuration public enum RotationGestureRecognizerDefaults { public static var configuration: ((UIRotationGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? } fileprivate typealias Defaults = RotationGestureRecognizerDefaults /// A `GestureRecognizerFactory` for `UIRotationGestureRecognizer` public struct RotationGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = UIRotationGestureRecognizer public let configuration: (UIRotationGestureRecognizer, RxGestureRecognizerDelegate) -> Void /** Initialiaze a `GestureRecognizerFactory` for `UIRotationGestureRecognizer` - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public init( configuration: ((UIRotationGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) { self.configuration = { gesture, delegate in configuration?(gesture, delegate) } } } extension AnyGestureRecognizerFactory { /** Returns an `AnyGestureRecognizerFactory` for `UIRotationGestureRecognizer` - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public static func rotation( configuration: ((UIRotationGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> AnyGestureRecognizerFactory { let gesture = RotationGestureRecognizerFactory( configuration: configuration ) return AnyGestureRecognizerFactory(gesture) } } public extension Reactive where Base: UIView { /** Returns an observable `UIRotationGestureRecognizer` events sequence - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public func rotationGesture( configuration: ((UIRotationGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> ControlEvent { return gesture(RotationGestureRecognizerFactory( configuration: configuration )) } } public extension ObservableType where E: UIRotationGestureRecognizer { /** Maps the observable `GestureRecognizer` events sequence to an observable sequence of rotation values of the gesture in radians alongside the gesture velocity. */ public func asRotation() -> Observable<(rotation: CGFloat, velocity: CGFloat)> { return self.map { gesture in return (gesture.rotation, gesture.velocity) } } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UIScreenEdgePanGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa /// Default values for `UIScreenEdgePanGestureRecognizer` configuration public enum UIScreenEdgePanGestureRecognizerDefaults { public static var configuration: ((UIScreenEdgePanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? } fileprivate typealias Defaults = UIScreenEdgePanGestureRecognizerDefaults /// A `GestureRecognizerFactory` for `UIScreenEdgePanGestureRecognizer` public struct ScreenEdgePanGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = UIScreenEdgePanGestureRecognizer public let configuration: (UIScreenEdgePanGestureRecognizer, RxGestureRecognizerDelegate) -> Void /** Initialiaze a `GestureRecognizerFactory` for `UIScreenEdgePanGestureRecognizer` - parameter edges: The edges on which this gesture recognizes, relative to the current interface orientation - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public init( edges: UIRectEdge, configuration: ((UIScreenEdgePanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) { self.configuration = { gestureRecognizer, delegate in gestureRecognizer.edges = edges configuration?(gestureRecognizer, delegate) } } } extension AnyGestureRecognizerFactory { /** Returns an `AnyGestureRecognizerFactory` for `UIScreenEdgePanGestureRecognizer` - parameter edges: The edges on which this gesture recognizes, relative to the current interface orientation - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public static func screenEdgePan( edges: UIRectEdge, configuration: ((UIScreenEdgePanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> AnyGestureRecognizerFactory { let gesture = ScreenEdgePanGestureRecognizerFactory( edges: edges, configuration: configuration ) return AnyGestureRecognizerFactory(gesture) } } public extension Reactive where Base: UIView { /** Returns an observable `UIScreenEdgePanGestureRecognizer` events sequence - parameter edges: The edges on which this gesture recognizes, relative to the current interface orientation - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public func screenEdgePanGesture( edges: UIRectEdge, configuration: ((UIScreenEdgePanGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> ControlEvent { return gesture(ScreenEdgePanGestureRecognizerFactory( edges: edges, configuration: configuration )) } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UISwipeGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa /// Default values for `UISwipeGestureRecognizer` configuration public enum UISwipeGestureRecognizerDefaults { public static var numberOfTouchesRequired: Int = 1 public static var configuration: ((UISwipeGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? } fileprivate typealias Defaults = UISwipeGestureRecognizerDefaults /// A `GestureRecognizerFactory` for `UISwipeGestureRecognizer` public struct SwipeGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = UISwipeGestureRecognizer public let configuration: (UISwipeGestureRecognizer, RxGestureRecognizerDelegate) -> Void /** Initialiaze a `GestureRecognizerFactory` for `UISwipeGestureRecognizer` - parameter numberOfTouchesRequired: The number of fingers required to match - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public init( _ direction: UISwipeGestureRecognizerDirection, numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, configuration: ((UISwipeGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) { self.configuration = { gesture, delegate in gesture.direction = direction gesture.numberOfTouchesRequired = numberOfTouchesRequired configuration?(gesture, delegate) } } } extension AnyGestureRecognizerFactory { /** Returns an `AnyGestureRecognizerFactory` for `UISwipeGestureRecognizer` - parameter numberOfTouchesRequired: The number of fingers required to match - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public static func swipe( _ direction: UISwipeGestureRecognizerDirection, numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, configuration: ((UISwipeGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> AnyGestureRecognizerFactory { let gesture = SwipeGestureRecognizerFactory( direction, numberOfTouchesRequired: numberOfTouchesRequired, configuration: configuration ) return AnyGestureRecognizerFactory(gesture) } } public extension Reactive where Base: UIView { /** Returns an observable `UISwipeGestureRecognizerDirection` events sequence - parameter numberOfTouchesRequired: The number of fingers required to match - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public func swipeGesture( _ direction: UISwipeGestureRecognizerDirection, numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, configuration: ((UISwipeGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> ControlEvent { return gesture(SwipeGestureRecognizerFactory( direction, numberOfTouchesRequired: numberOfTouchesRequired, configuration: configuration )) } } ================================================ FILE: Pods/RxGesture/Pod/Classes/iOS/UITapGestureRecognizer+RxGesture.swift ================================================ // Copyright (c) RxSwiftCommunity // 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. import UIKit import RxSwift import RxCocoa /// Default values for `UITapGestureRecognizer` configuration public enum UITapGestureRecognizerDefaults { public static var numberOfTouchesRequired: Int = 1 public static var numberOfTapsRequired: Int = 1 public static var configuration: ((UITapGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? } fileprivate typealias Defaults = UITapGestureRecognizerDefaults /// A `GestureRecognizerFactory` for `UITapGestureRecognizer` public struct TapGestureRecognizerFactory: GestureRecognizerFactory { public typealias Gesture = UITapGestureRecognizer public let configuration: (UITapGestureRecognizer, RxGestureRecognizerDelegate) -> Void /** Initialiaze a `GestureRecognizerFactory` for `UITapGestureRecognizer` - parameter numberOfTouchesRequired: The number of fingers required to match - parameter numberOfTapsRequired: The number of taps required to match - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public init( numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, numberOfTapsRequired: Int = Defaults.numberOfTapsRequired, configuration: ((UITapGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) { self.configuration = { gesture, delegate in gesture.numberOfTouchesRequired = numberOfTouchesRequired gesture.numberOfTapsRequired = numberOfTapsRequired configuration?(gesture, delegate) } } } extension AnyGestureRecognizerFactory { /** Returns an `AnyGestureRecognizerFactory` for `UITapGestureRecognizer` - parameter numberOfTouchesRequired: The number of fingers required to match - parameter numberOfTapsRequired: The number of taps required to match - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public static func tap( numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, numberOfTapsRequired: Int = Defaults.numberOfTapsRequired, configuration: ((UITapGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> AnyGestureRecognizerFactory { let gesture = TapGestureRecognizerFactory( numberOfTouchesRequired: numberOfTouchesRequired, numberOfTapsRequired: numberOfTapsRequired, configuration: configuration ) return AnyGestureRecognizerFactory(gesture) } } public extension Reactive where Base: UIView { /** Returns an observable `UITapGestureRecognizer` events sequence - parameter numberOfTouchesRequired: The number of fingers required to match - parameter numberOfTapsRequired: The number of taps required to match - parameter configuration: A closure that allows to fully configure the gesture recognizer */ public func tapGesture( numberOfTouchesRequired: Int = Defaults.numberOfTouchesRequired, numberOfTapsRequired: Int = Defaults.numberOfTapsRequired, configuration: ((UITapGestureRecognizer, RxGestureRecognizerDelegate) -> Void)? = Defaults.configuration ) -> ControlEvent { return gesture(TapGestureRecognizerFactory( numberOfTouchesRequired: numberOfTouchesRequired, numberOfTapsRequired: numberOfTapsRequired, configuration: configuration )) } } ================================================ FILE: Pods/RxGesture/README.md ================================================ # RxGesture [![Version](https://img.shields.io/cocoapods/v/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) [![License](https://img.shields.io/cocoapods/l/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) [![Platform](https://img.shields.io/cocoapods/p/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) ## Usage ![](Pod/Assets/demo.gif) To run the example project, clone the repo, in the __Example__ folder open `RxGesture.xcworkspace`. You _might_ need to run `pod install` from the Example directory first. --- __RxGesture__ allows you to easily turn any view into a tappable or swipeable control like so: ```swift view.rx .tapGesture() .when(.recognized) .subscribe(onNext: { _ in //react to taps }) .disposed(by: stepBag) ``` You can also react to more than one gesture. For example to dismiss a photo preview you might want to do that when the user taps it, or swipes up or down: ```swift view.rx .anyGesture(.tap(), .swipe([.up, .down])) .when(.recognized) .subscribe(onNext: { _ in //dismiss presented photo }) .disposed(by: stepBag) ``` `rx.gesture` is defined as `Observable` where `G` is the actual type of the gesture recognizer so what it emits is the gesture recognizer itself (handy if want to call methods like `asLocation(in view:)` or `asTranslation(in view:)`) #### On iOS, RxGesture supports: ```swift view.rx.tapGesture() -> ControlEvent view.rx.pinchGesture() -> ControlEvent view.rx.swipeGesture(.left) -> ControlEvent view.rx.panGesture() -> ControlEvent view.rx.longPressGesture() -> ControlEvent view.rx.rotationGesture() -> ControlEvent view.rx.screenEdgePanGesture() -> ControlEvent view.rx.anyGesture(.tap(), ...) -> ControlEvent view.rx.anyGesture(.pinch(), ...) -> ControlEvent view.rx.anyGesture(.swipe(.left), ...) -> ControlEvent view.rx.anyGesture(.pan(), ...) -> ControlEvent view.rx.anyGesture(.longPress(), ...) -> ControlEvent view.rx.anyGesture(.rotation(), ...) -> ControlEvent view.rx.anyGesture(.screenEdgePan(), ...) -> ControlEvent ``` #### On macOS, RxGesture supports: ```swift view.rx.clickGesture() -> ControlEvent view.rx.rightClickGesture() -> ControlEvent view.rx.panGesture() -> ControlEvent view.rx.pressGesture() -> ControlEvent view.rx.rotationGesture() -> ControlEvent view.rx.magnificationGesture() -> ControlEvent view.rx.anyGesture(.click(), ...) -> ControlEvent view.rx.anyGesture(.rightClick(), ...) -> ControlEvent view.rx.anyGesture(.pan(), ...) -> ControlEvent view.rx.anyGesture(.press(), ...) -> ControlEvent view.rx.anyGesture(.rotation(), ...) -> ControlEvent view.rx.anyGesture(.magnification(), ...) -> ControlEvent ``` ℹ️ If you use a gesture recognizer alone, prefer the `view.rx.fooGesture()` syntax over `view.rx.anyGesture(.foo())` because it returns the concrete `UIGestureRecognizer` subclass and avoid you to cast it in `subscribe()`. ## Filtering State By default, there is no filter on the state of the gesture recognizer. That means that you will always receive a first event with the initial state of the gesture recognizer (almost always `.possible`). Here are the preferred states that can be used for each kind of gestures (__iOS__ and __macOS__): Kind | States ---|--- `.tap()` `.click()` `.rightClick()` `.swipe()`| `.recognized` `.longPress()` `.press()` | `.began` `.pan()` `.pinch()` `.rotation()` `.magnification()` `.screenEdgePan()` | `.began` `.changed` `.ended` You usually filter the state using the `.when()` operator: ```swift view.rx.tapGesture().when(.recognized) view.rx.panGesture().when(.began, .changed, .ended) ``` If you are observing multiple gestures at once, you can use the `.when()` operator if you want to filter against the same state for __all__ gesture recognizers, or use the tuple syntax for individual filtering: ```swift view.rx .anyGesture(.tap(), .swipe([.up, .down])) .when(.recognized) .subscribe(onNext: { gesture in // Called whenever a tap, a swipe-up or a swipe-down is recognized (state == .recognized) }) .disposed(by: bag) view.rx .anyGesture( (.tap(), when: .recognized), (.pan(), when: .ended) ) .subscribe(onNext: { gesture in // Called whenever: // - a tap is recognized (state == .recognized) // - or a pan is ended (state == .ended) }) .disposed(by: bag) ``` __The demo app includes examples for all recognizers ➡️ [iOS](Example/RxGesture/ViewController.swift), [macOS](Example/RxGesture-OSX/ViewController.swift)__. ## Delegate customization ### Lightweight customization Each gesture recognizer has a default `RxGestureRecognizerDelegate`. It allows you to customize every delegate method using a policy: - `.always` will return `true` to the corresponding delegate method - `.never` will return `false` to the corresponding delegate method - `.custom` takes an associated closure that will be executed to return a value to the corresponding delegate method Here are the available policies with their corresponding delegate method: ```swift beginPolicy -> gestureRecognizerShouldBegin(:_) touchReceptionPolicy -> gestureRecognizer(_:shouldReceive:) selfFailureRequirementPolicy -> gestureRecognizer(_:shouldBeRequiredToFailBy:) otherFailureRequirementPolicy -> gestureRecognizer(_:shouldRequireFailureOf:) simultaneousRecognitionPolicy -> gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) eventRecognitionAttemptPolicy -> gestureRecognizer(_:shouldAttemptToRecognizeWith:) // macOS only pressReceptionPolicy -> gestureRecognizer(_:shouldReceive:) // iOS only ``` This delegate can be customized in the configuration closure: ```swift view.rx.tapGesture(configuration: { gestureRecognizer, delegate in delegate.simultaneousRecognitionPolicy = .always // (default value) // or delegate.simultaneousRecognitionPolicy = .never // or delegate.simultaneousRecognitionPolicy = .custom { gestureRecognizer, otherGestureRecognizer in return otherGestureRecognizer is UIPanGestureRecognizer } delegate.otherFailureRequirementPolicy = .custom { gestureRecognizer, otherGestureRecognizer in return otherGestureRecognizer is UILongPressGestureRecognizer } }) ``` Default values can be found in [`RxGestureRecognizerDelegate.swift`](Pod/Classes/RxGestureRecognizerDelegate.swift#L56). ### Full customization You can also replace the default delegate by your own, or remove it. ```swift view.rx.tapGesture { [unowned self] gestureRecognizer, delegate in gestureRecognizer.delegate = nil // or gestureRecognizer.delegate = self } ``` ## Requirements This library depends on both __RxSwift__ and __RxCocoa__. ## Installation ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) Add this to `Podfile` ```swift pod "RxGesture" ``` ```bash $ pod install ``` ### [Carthage](https://github.com/Carthage/Carthage) Add this to `Cartfile` ``` github "RxSwiftCommunity/RxGesture" ~> 1.1.1 ``` ```bash $ carthage update ``` ## Thanks Everyone in the RxSwift Slack channel 💯 ## License RxGesture is available under the MIT license. See the LICENSE file for more info. ================================================ FILE: Pods/RxSwift/LICENSE.md ================================================ **The MIT License** **Copyright © 2015 Krunoslav Zaher** **All rights reserved.** 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. ================================================ FILE: Pods/RxSwift/Platform/DataStructures/Bag.swift ================================================ // // Bag.swift // Platform // // Created by Krunoslav Zaher on 2/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import Swift let arrayDictionaryMaxSize = 30 struct BagKey { /** Unique identifier for object added to `Bag`. It's underlying type is UInt64. If we assume there in an idealized CPU that works at 4GHz, it would take ~150 years of continuous running time for it to overflow. */ fileprivate let rawValue: UInt64 } /** Data structure that represents a bag of elements typed `T`. Single element can be stored multiple times. Time and space complexity of insertion an deletion is O(n). It is suitable for storing small number of elements. */ struct Bag : CustomDebugStringConvertible { /// Type of identifier for inserted elements. typealias KeyType = BagKey typealias Entry = (key: BagKey, value: T) fileprivate var _nextKey: BagKey = BagKey(rawValue: 0) // data // first fill inline variables var _key0: BagKey? = nil var _value0: T? = nil // then fill "array dictionary" var _pairs = ContiguousArray() // last is sparse dictionary var _dictionary: [BagKey : T]? = nil var _onlyFastPath = true /// Creates new empty `Bag`. init() { } /** Inserts `value` into bag. - parameter element: Element to insert. - returns: Key that can be used to remove element from bag. */ mutating func insert(_ element: T) -> BagKey { let key = _nextKey _nextKey = BagKey(rawValue: _nextKey.rawValue &+ 1) if _key0 == nil { _key0 = key _value0 = element return key } _onlyFastPath = false if _dictionary != nil { _dictionary![key] = element return key } if _pairs.count < arrayDictionaryMaxSize { _pairs.append((key: key, value: element)) return key } if _dictionary == nil { _dictionary = [:] } _dictionary![key] = element return key } /// - returns: Number of elements in bag. var count: Int { let dictionaryCount: Int = _dictionary?.count ?? 0 return (_value0 != nil ? 1 : 0) + _pairs.count + dictionaryCount } /// Removes all elements from bag and clears capacity. mutating func removeAll() { _key0 = nil _value0 = nil _pairs.removeAll(keepingCapacity: false) _dictionary?.removeAll(keepingCapacity: false) } /** Removes element with a specific `key` from bag. - parameter key: Key that identifies element to remove from bag. - returns: Element that bag contained, or nil in case element was already removed. */ mutating func removeKey(_ key: BagKey) -> T? { if _key0 == key { _key0 = nil let value = _value0! _value0 = nil return value } if let existingObject = _dictionary?.removeValue(forKey: key) { return existingObject } for i in 0 ..< _pairs.count { if _pairs[i].key == key { let value = _pairs[i].value _pairs.remove(at: i) return value } } return nil } } extension Bag { /// A textual representation of `self`, suitable for debugging. var debugDescription : String { return "\(self.count) elements in Bag" } } extension Bag { /// Enumerates elements inside the bag. /// /// - parameter action: Enumeration closure. func forEach(_ action: (T) -> Void) { if _onlyFastPath { if let value0 = _value0 { action(value0) } return } let value0 = _value0 let dictionary = _dictionary if let value0 = value0 { action(value0) } for i in 0 ..< _pairs.count { action(_pairs[i].value) } if dictionary?.count ?? 0 > 0 { for element in dictionary!.values { action(element) } } } } extension BagKey: Hashable { var hashValue: Int { return rawValue.hashValue } } func ==(lhs: BagKey, rhs: BagKey) -> Bool { return lhs.rawValue == rhs.rawValue } ================================================ FILE: Pods/RxSwift/Platform/DataStructures/InfiniteSequence.swift ================================================ // // InfiniteSequence.swift // Platform // // Created by Krunoslav Zaher on 6/13/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Sequence that repeats `repeatedValue` infinite number of times. struct InfiniteSequence : Sequence { typealias Element = E typealias Iterator = AnyIterator private let _repeatedValue: E init(repeatedValue: E) { _repeatedValue = repeatedValue } func makeIterator() -> Iterator { let repeatedValue = _repeatedValue return AnyIterator { return repeatedValue } } } ================================================ FILE: Pods/RxSwift/Platform/DataStructures/PriorityQueue.swift ================================================ // // PriorityQueue.swift // Platform // // Created by Krunoslav Zaher on 12/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // struct PriorityQueue { private let _hasHigherPriority: (Element, Element) -> Bool private let _isEqual: (Element, Element) -> Bool fileprivate var _elements = [Element]() init(hasHigherPriority: @escaping (Element, Element) -> Bool, isEqual: @escaping (Element, Element) -> Bool) { _hasHigherPriority = hasHigherPriority _isEqual = isEqual } mutating func enqueue(_ element: Element) { _elements.append(element) bubbleToHigherPriority(_elements.count - 1) } func peek() -> Element? { return _elements.first } var isEmpty: Bool { return _elements.count == 0 } mutating func dequeue() -> Element? { guard let front = peek() else { return nil } removeAt(0) return front } mutating func remove(_ element: Element) { for i in 0 ..< _elements.count { if _isEqual(_elements[i], element) { removeAt(i) return } } } private mutating func removeAt(_ index: Int) { let removingLast = index == _elements.count - 1 if !removingLast { #if swift(>=3.2) _elements.swapAt(index, _elements.count - 1) #else swap(&_elements[index], &_elements[_elements.count - 1]) #endif } _ = _elements.popLast() if !removingLast { bubbleToHigherPriority(index) bubbleToLowerPriority(index) } } private mutating func bubbleToHigherPriority(_ initialUnbalancedIndex: Int) { precondition(initialUnbalancedIndex >= 0) precondition(initialUnbalancedIndex < _elements.count) var unbalancedIndex = initialUnbalancedIndex while unbalancedIndex > 0 { let parentIndex = (unbalancedIndex - 1) / 2 guard _hasHigherPriority(_elements[unbalancedIndex], _elements[parentIndex]) else { break } #if swift(>=3.2) _elements.swapAt(unbalancedIndex, parentIndex) #else swap(&_elements[unbalancedIndex], &_elements[parentIndex]) #endif unbalancedIndex = parentIndex } } private mutating func bubbleToLowerPriority(_ initialUnbalancedIndex: Int) { precondition(initialUnbalancedIndex >= 0) precondition(initialUnbalancedIndex < _elements.count) var unbalancedIndex = initialUnbalancedIndex while true { let leftChildIndex = unbalancedIndex * 2 + 1 let rightChildIndex = unbalancedIndex * 2 + 2 var highestPriorityIndex = unbalancedIndex if leftChildIndex < _elements.count && _hasHigherPriority(_elements[leftChildIndex], _elements[highestPriorityIndex]) { highestPriorityIndex = leftChildIndex } if rightChildIndex < _elements.count && _hasHigherPriority(_elements[rightChildIndex], _elements[highestPriorityIndex]) { highestPriorityIndex = rightChildIndex } guard highestPriorityIndex != unbalancedIndex else { break } #if swift(>=3.2) _elements.swapAt(highestPriorityIndex, unbalancedIndex) #else swap(&_elements[highestPriorityIndex], &_elements[unbalancedIndex]) #endif unbalancedIndex = highestPriorityIndex } } } extension PriorityQueue : CustomDebugStringConvertible { var debugDescription: String { return _elements.debugDescription } } ================================================ FILE: Pods/RxSwift/Platform/DataStructures/Queue.swift ================================================ // // Queue.swift // Platform // // Created by Krunoslav Zaher on 3/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /** Data structure that represents queue. Complexity of `enqueue`, `dequeue` is O(1) when number of operations is averaged over N operations. Complexity of `peek` is O(1). */ struct Queue: Sequence { /// Type of generator. typealias Generator = AnyIterator private let _resizeFactor = 2 private var _storage: ContiguousArray private var _count = 0 private var _pushNextIndex = 0 private let _initialCapacity: Int /** Creates new queue. - parameter capacity: Capacity of newly created queue. */ init(capacity: Int) { _initialCapacity = capacity _storage = ContiguousArray(repeating: nil, count: capacity) } private var dequeueIndex: Int { let index = _pushNextIndex - count return index < 0 ? index + _storage.count : index } /// - returns: Is queue empty. var isEmpty: Bool { return count == 0 } /// - returns: Number of elements inside queue. var count: Int { return _count } /// - returns: Element in front of a list of elements to `dequeue`. func peek() -> T { precondition(count > 0) return _storage[dequeueIndex]! } mutating private func resizeTo(_ size: Int) { var newStorage = ContiguousArray(repeating: nil, count: size) let count = _count let dequeueIndex = self.dequeueIndex let spaceToEndOfQueue = _storage.count - dequeueIndex // first batch is from dequeue index to end of array let countElementsInFirstBatch = Swift.min(count, spaceToEndOfQueue) // second batch is wrapped from start of array to end of queue let numberOfElementsInSecondBatch = count - countElementsInFirstBatch newStorage[0 ..< countElementsInFirstBatch] = _storage[dequeueIndex ..< (dequeueIndex + countElementsInFirstBatch)] newStorage[countElementsInFirstBatch ..< (countElementsInFirstBatch + numberOfElementsInSecondBatch)] = _storage[0 ..< numberOfElementsInSecondBatch] _count = count _pushNextIndex = count _storage = newStorage } /// Enqueues `element`. /// /// - parameter element: Element to enqueue. mutating func enqueue(_ element: T) { if count == _storage.count { resizeTo(Swift.max(_storage.count, 1) * _resizeFactor) } _storage[_pushNextIndex] = element _pushNextIndex += 1 _count += 1 if _pushNextIndex >= _storage.count { _pushNextIndex -= _storage.count } } private mutating func dequeueElementOnly() -> T { precondition(count > 0) let index = dequeueIndex defer { _storage[index] = nil _count -= 1 } return _storage[index]! } /// Dequeues element or throws an exception in case queue is empty. /// /// - returns: Dequeued element. mutating func dequeue() -> T? { if self.count == 0 { return nil } defer { let downsizeLimit = _storage.count / (_resizeFactor * _resizeFactor) if _count < downsizeLimit && downsizeLimit >= _initialCapacity { resizeTo(_storage.count / _resizeFactor) } } return dequeueElementOnly() } /// - returns: Generator of contained elements. func makeIterator() -> AnyIterator { var i = dequeueIndex var count = _count return AnyIterator { if count == 0 { return nil } defer { count -= 1 i += 1 } if i >= self._storage.count { i -= self._storage.count } return self._storage[i] } } } ================================================ FILE: Pods/RxSwift/Platform/DispatchQueue+Extensions.swift ================================================ // // DispatchQueue+Extensions.swift // Platform // // Created by Krunoslav Zaher on 10/22/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Dispatch extension DispatchQueue { private static var token: DispatchSpecificKey<()> = { let key = DispatchSpecificKey<()>() DispatchQueue.main.setSpecific(key: key, value: ()) return key }() static var isMain: Bool { return DispatchQueue.getSpecific(key: token) != nil } } ================================================ FILE: Pods/RxSwift/Platform/Platform.Darwin.swift ================================================ // // Platform.Darwin.swift // Platform // // Created by Krunoslav Zaher on 12/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin import class Foundation.Thread import func Foundation.OSAtomicCompareAndSwap32Barrier import func Foundation.OSAtomicIncrement32Barrier import func Foundation.OSAtomicDecrement32Barrier import protocol Foundation.NSCopying typealias AtomicInt = Int32 fileprivate func castToUInt32Pointer(_ pointer: UnsafeMutablePointer) -> UnsafeMutablePointer { let raw = UnsafeMutableRawPointer(pointer) return raw.assumingMemoryBound(to: UInt32.self) } let AtomicCompareAndSwap = OSAtomicCompareAndSwap32Barrier let AtomicIncrement = OSAtomicIncrement32Barrier let AtomicDecrement = OSAtomicDecrement32Barrier func AtomicOr(_ mask: UInt32, _ theValue : UnsafeMutablePointer) -> Int32 { return OSAtomicOr32OrigBarrier(mask, castToUInt32Pointer(theValue)) } func AtomicFlagSet(_ mask: UInt32, _ theValue : UnsafeMutablePointer) -> Bool { // just used to create a barrier OSAtomicXor32OrigBarrier(0, castToUInt32Pointer(theValue)) return (theValue.pointee & Int32(mask)) != 0 } extension Thread { static func setThreadLocalStorageValue(_ value: T?, forKey key: NSCopying ) { let currentThread = Thread.current let threadDictionary = currentThread.threadDictionary if let newValue = value { threadDictionary[key] = newValue } else { threadDictionary[key] = nil } } static func getThreadLocalStorageValueForKey(_ key: NSCopying) -> T? { let currentThread = Thread.current let threadDictionary = currentThread.threadDictionary return threadDictionary[key] as? T } } extension AtomicInt { func valueSnapshot() -> Int32 { return self } } #endif ================================================ FILE: Pods/RxSwift/Platform/Platform.Linux.swift ================================================ // // Platform.Linux.swift // Platform // // Created by Krunoslav Zaher on 12/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(Linux) import XCTest import Glibc import SwiftShims import class Foundation.Thread final class AtomicInt { typealias IntegerLiteralType = Int fileprivate var value: Int32 = 0 fileprivate var _lock = RecursiveLock() func lock() { _lock.lock() } func unlock() { _lock.unlock() } func valueSnapshot() -> Int32 { return value } } extension AtomicInt: ExpressibleByIntegerLiteral { convenience init(integerLiteral value: Int) { self.init() self.value = Int32(value) } } func >(lhs: AtomicInt, rhs: Int32) -> Bool { return lhs.value > rhs } func ==(lhs: AtomicInt, rhs: Int32) -> Bool { return lhs.value == rhs } func AtomicFlagSet(_ mask: UInt32, _ atomic: inout AtomicInt) -> Bool { atomic.lock(); defer { atomic.unlock() } return (atomic.value & Int32(mask)) != 0 } func AtomicOr(_ mask: UInt32, _ atomic: inout AtomicInt) -> Int32 { atomic.lock(); defer { atomic.unlock() } let value = atomic.value atomic.value |= Int32(mask) return value } func AtomicIncrement(_ atomic: inout AtomicInt) -> Int32 { atomic.lock(); defer { atomic.unlock() } atomic.value += 1 return atomic.value } func AtomicDecrement(_ atomic: inout AtomicInt) -> Int32 { atomic.lock(); defer { atomic.unlock() } atomic.value -= 1 return atomic.value } func AtomicCompareAndSwap(_ l: Int32, _ r: Int32, _ atomic: inout AtomicInt) -> Bool { atomic.lock(); defer { atomic.unlock() } if atomic.value == l { atomic.value = r return true } return false } extension Thread { static func setThreadLocalStorageValue(_ value: T?, forKey key: String) { let currentThread = Thread.current var threadDictionary = currentThread.threadDictionary if let newValue = value { threadDictionary[key] = newValue } else { threadDictionary[key] = nil } currentThread.threadDictionary = threadDictionary } static func getThreadLocalStorageValueForKey(_ key: String) -> T? { let currentThread = Thread.current let threadDictionary = currentThread.threadDictionary return threadDictionary[key] as? T } } #endif ================================================ FILE: Pods/RxSwift/Platform/RecursiveLock.swift ================================================ // // RecursiveLock.swift // Platform // // Created by Krunoslav Zaher on 12/18/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import class Foundation.NSRecursiveLock #if TRACE_RESOURCES class RecursiveLock: NSRecursiveLock { override init() { _ = Resources.incrementTotal() super.init() } override func lock() { super.lock() _ = Resources.incrementTotal() } override func unlock() { super.unlock() _ = Resources.decrementTotal() } deinit { _ = Resources.decrementTotal() } } #else typealias RecursiveLock = NSRecursiveLock #endif ================================================ FILE: Pods/RxSwift/README.md ================================================ Miss Electric Eel 2016 RxSwift: ReactiveX for Swift ====================================== [![Travis CI](https://travis-ci.org/ReactiveX/RxSwift.svg?branch=master)](https://travis-ci.org/ReactiveX/RxSwift) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux-333333.svg) ![pod](https://img.shields.io/cocoapods/v/RxSwift.svg) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) * RxSwift 4.x / Swift 4.x can be found in [**rxswift4.0-swift4.0** branch](https://github.com/ReactiveX/RxSwift/tree/rxswift4.0-swift4.0). * RxSwift 3.x / Swift 3.x can be found in [**master** branch](https://github.com/ReactiveX/RxSwift/tree/master). Rx is a [generic abstraction of computation](https://youtu.be/looJcaeboBY) expressed through `Observable` interface. This is a Swift version of [Rx](https://github.com/Reactive-Extensions/Rx.NET). It tries to port as many concepts from the original version as possible, but some concepts were adapted for more pleasant and performant integration with iOS/macOS environment. Cross platform documentation can be found on [ReactiveX.io](http://reactivex.io/). Like the original Rx, its intention is to enable easy composition of asynchronous operations and event/data streams. KVO observing, async operations and streams are all unified under [abstraction of sequence](Documentation/GettingStarted.md#observables-aka-sequences). This is the reason why Rx is so simple, elegant and powerful. ## I came here because I want to ... ###### ... understand * [why use rx?](Documentation/Why.md) * [the basics, getting started with RxSwift](Documentation/GettingStarted.md) * [traits](Documentation/Traits.md) - what are `Single`, `Completable`, `Maybe`, `Driver`, `ControlProperty`, and `Variable` ... and why do they exist? * [testing](Documentation/UnitTests.md) * [tips and common errors](Documentation/Tips.md) * [debugging](Documentation/GettingStarted.md#debugging) * [the math behind Rx](Documentation/MathBehindRx.md) * [what are hot and cold observable sequences?](Documentation/HotAndColdObservables.md) ###### ... install * Integrate RxSwift/RxCocoa with my app. [Installation Guide](#installation) ###### ... hack around * with the example app. [Running Example App](Documentation/ExampleApp.md) * with operators in playgrounds. [Playgrounds](Documentation/Playgrounds.md) ###### ... interact * All of this is great, but it would be nice to talk with other people using RxSwift and exchange experiences.
[![Slack channel](http://rxswift-slack.herokuapp.com/badge.svg)](http://rxswift-slack.herokuapp.com/) [Join Slack Channel](http://rxswift-slack.herokuapp.com) * Report a problem using the library. [Open an Issue With Bug Template](.github/ISSUE_TEMPLATE.md) * Request a new feature. [Open an Issue With Feature Request Template](Documentation/NewFeatureRequestTemplate.md) ###### ... compare * [with other libraries](Documentation/ComparisonWithOtherLibraries.md). ###### ... find compatible * libraries from [RxSwiftCommunity](https://github.com/RxSwiftCommunity). * [Pods using RxSwift](https://cocoapods.org/?q=uses%3Arxswift). ###### ... see the broader vision * Does this exist for Android? [RxJava](https://github.com/ReactiveX/RxJava) * Where is all of this going, what is the future, what about reactive architectures, how do you design entire apps this way? [Cycle.js](https://github.com/cyclejs/cycle-core) - this is javascript, but [RxJS](https://github.com/Reactive-Extensions/RxJS) is javascript version of Rx. ## Usage
Here's an example In Action
Define search for GitHub repositories ...
let searchResults = searchBar.rx.text.orEmpty
    .throttle(0.3, scheduler: MainScheduler.instance)
    .distinctUntilChanged()
    .flatMapLatest { query -> Observable<[Repository]> in
        if query.isEmpty {
            return .just([])
        }
        return searchGitHub(query)
            .catchErrorJustReturn([])
    }
    .observeOn(MainScheduler.instance)
... then bind the results to your tableview
searchResults
    .bind(to: tableView.rx.items(cellIdentifier: "Cell")) {
        (index, repository: Repository, cell) in
        cell.textLabel?.text = repository.name
        cell.detailTextLabel?.text = repository.url
    }
    .disposed(by: disposeBag)
## Requirements * Xcode 8.0 * Swift 3.0 * Swift 2.3 ([use `rxswift-2.0` branch](https://github.com/ReactiveX/RxSwift/tree/rxswift-2.0) instead) ## Installation Rx doesn't contain any external dependencies. These are currently the supported options: ### Manual Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build everything and run the sample app ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) **Tested with `pod --version`: `1.1.1`** ```ruby # Podfile use_frameworks! target 'YOUR_TARGET_NAME' do pod 'RxSwift', '~> 3.0' pod 'RxCocoa', '~> 3.0' end # RxTests and RxBlocking make the most sense in the context of unit/integration tests target 'YOUR_TESTING_TARGET' do pod 'RxBlocking', '~> 3.0' pod 'RxTest', '~> 3.0' end ``` Replace `YOUR_TARGET_NAME` and then, in the `Podfile` directory, type: ```bash $ pod install ``` ### [Carthage](https://github.com/Carthage/Carthage) **Tested with `carthage version`: `0.18.1`** Add this to `Cartfile` ``` github "ReactiveX/RxSwift" ~> 3.0 ``` ```bash $ carthage update ``` ### [Swift Package Manager](https://github.com/apple/swift-package-manager) **Tested with `swift build --version`: `3.0.0 (swiftpm-19)`** Create a `Package.swift` file. ```swift import PackageDescription let package = Package( name: "RxTestProject", targets: [], dependencies: [ .Package(url: "https://github.com/ReactiveX/RxSwift.git", majorVersion: 3) ] ) ``` ```bash $ swift build ``` To build or test a module with RxTest dependency, set `TEST=1`. ([RxSwift >= 3.4.2](https://github.com/ReactiveX/RxSwift/releases/tag/3.4.2)) ```bash $ TEST=1 swift test ``` ### Manually using git submodules * Add RxSwift as a submodule ```bash $ git submodule add git@github.com:ReactiveX/RxSwift.git ``` * Drag `Rx.xcodeproj` into Project Navigator * Go to `Project > Targets > Build Phases > Link Binary With Libraries`, click `+` and select `RxSwift-[Platform]` and `RxCocoa-[Platform]` targets ## References * [http://reactivex.io/](http://reactivex.io/) * [Reactive Extensions GitHub (GitHub)](https://github.com/Reactive-Extensions) * [RxSwift RayWenderlich.com Book](https://store.raywenderlich.com/products/rxswift) * [Boxue.io RxSwift Online Course](https://boxueio.com/series/rxswift-101) (Chinese 🇨🇳) * [Erik Meijer (Wikipedia)](http://en.wikipedia.org/wiki/Erik_Meijer_%28computer_scientist%29) * [Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx) (video)](https://youtu.be/looJcaeboBY) * [Reactive Programming Overview (Jafar Husain from Netflix)](https://www.youtube.com/watch?v=dwP1TNXE6fc) * [Subject/Observer is Dual to Iterator (paper)](http://csl.stanford.edu/~christos/pldi2010.fit/meijer.duality.pdf) * [Rx standard sequence operators visualized (visualization tool)](http://rxmarbles.com/) * [Haskell](https://www.haskell.org/) ================================================ FILE: Pods/RxSwift/RxSwift/AnyObserver.swift ================================================ // // AnyObserver.swift // RxSwift // // Created by Krunoslav Zaher on 2/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// A type-erased `ObserverType`. /// /// Forwards operations to an arbitrary underlying observer with the same `Element` type, hiding the specifics of the underlying observer type. public struct AnyObserver : ObserverType { /// The type of elements in sequence that observer can observe. public typealias E = Element /// Anonymous event handler type. public typealias EventHandler = (Event) -> Void private let observer: EventHandler /// Construct an instance whose `on(event)` calls `eventHandler(event)` /// /// - parameter eventHandler: Event handler that observes sequences events. public init(eventHandler: @escaping EventHandler) { self.observer = eventHandler } /// Construct an instance whose `on(event)` calls `observer.on(event)` /// /// - parameter observer: Observer that receives sequence events. public init(_ observer: O) where O.E == Element { self.observer = observer.on } /// Send `event` to this observer. /// /// - parameter event: Event instance. public func on(_ event: Event) { return self.observer(event) } /// Erases type of observer and returns canonical observer. /// /// - returns: type erased observer. public func asObserver() -> AnyObserver { return self } } extension AnyObserver { /// Collection of `AnyObserver`s typealias s = Bag<(Event) -> ()> } extension ObserverType { /// Erases type of observer and returns canonical observer. /// /// - returns: type erased observer. public func asObserver() -> AnyObserver { return AnyObserver(self) } /// Transforms observer of type R to type E using custom transform method. /// Each event sent to result observer is transformed and sent to `self`. /// /// - returns: observer that transforms events. public func mapObserver(_ transform: @escaping (R) throws -> E) -> AnyObserver { return AnyObserver { e in self.on(e.map(transform)) } } } ================================================ FILE: Pods/RxSwift/RxSwift/Cancelable.swift ================================================ // // Cancelable.swift // RxSwift // // Created by Krunoslav Zaher on 3/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents disposable resource with state tracking. public protocol Cancelable : Disposable { /// Was resource disposed. var isDisposed: Bool { get } } ================================================ FILE: Pods/RxSwift/RxSwift/Concurrency/AsyncLock.swift ================================================ // // AsyncLock.swift // RxSwift // // Created by Krunoslav Zaher on 3/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /** In case nobody holds this lock, the work will be queued and executed immediately on thread that is requesting lock. In case there is somebody currently holding that lock, action will be enqueued. When owned of the lock finishes with it's processing, it will also execute and pending work. That means that enqueued work could possibly be executed later on a different thread. */ final class AsyncLock : Disposable , Lock , SynchronizedDisposeType { typealias Action = () -> Void var _lock = SpinLock() private var _queue: Queue = Queue(capacity: 0) private var _isExecuting: Bool = false private var _hasFaulted: Bool = false // lock { func lock() { _lock.lock() } func unlock() { _lock.unlock() } // } private func enqueue(_ action: I) -> I? { _lock.lock(); defer { _lock.unlock() } // { if _hasFaulted { return nil } if _isExecuting { _queue.enqueue(action) return nil } _isExecuting = true return action // } } private func dequeue() -> I? { _lock.lock(); defer { _lock.unlock() } // { if _queue.count > 0 { return _queue.dequeue() } else { _isExecuting = false return nil } // } } func invoke(_ action: I) { let firstEnqueuedAction = enqueue(action) if let firstEnqueuedAction = firstEnqueuedAction { firstEnqueuedAction.invoke() } else { // action is enqueued, it's somebody else's concern now return } while true { let nextAction = dequeue() if let nextAction = nextAction { nextAction.invoke() } else { return } } } func dispose() { synchronizedDispose() } func _synchronized_dispose() { _queue = Queue(capacity: 0) _hasFaulted = true } } ================================================ FILE: Pods/RxSwift/RxSwift/Concurrency/Lock.swift ================================================ // // Lock.swift // RxSwift // // Created by Krunoslav Zaher on 3/31/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol Lock { func lock() func unlock() } // https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000321.html typealias SpinLock = RecursiveLock extension RecursiveLock : Lock { @inline(__always) final func performLocked(_ action: () -> Void) { lock(); defer { unlock() } action() } @inline(__always) final func calculateLocked(_ action: () -> T) -> T { lock(); defer { unlock() } return action() } @inline(__always) final func calculateLockedOrFail(_ action: () throws -> T) throws -> T { lock(); defer { unlock() } let result = try action() return result } } ================================================ FILE: Pods/RxSwift/RxSwift/Concurrency/LockOwnerType.swift ================================================ // // LockOwnerType.swift // RxSwift // // Created by Krunoslav Zaher on 10/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol LockOwnerType : class, Lock { var _lock: RecursiveLock { get } } extension LockOwnerType { func lock() { _lock.lock() } func unlock() { _lock.unlock() } } ================================================ FILE: Pods/RxSwift/RxSwift/Concurrency/SynchronizedDisposeType.swift ================================================ // // SynchronizedDisposeType.swift // RxSwift // // Created by Krunoslav Zaher on 10/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol SynchronizedDisposeType : class, Disposable, Lock { func _synchronized_dispose() } extension SynchronizedDisposeType { func synchronizedDispose() { lock(); defer { unlock() } _synchronized_dispose() } } ================================================ FILE: Pods/RxSwift/RxSwift/Concurrency/SynchronizedOnType.swift ================================================ // // SynchronizedOnType.swift // RxSwift // // Created by Krunoslav Zaher on 10/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol SynchronizedOnType : class, ObserverType, Lock { func _synchronized_on(_ event: Event) } extension SynchronizedOnType { func synchronizedOn(_ event: Event) { lock(); defer { unlock() } _synchronized_on(event) } } ================================================ FILE: Pods/RxSwift/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift ================================================ // // SynchronizedUnsubscribeType.swift // RxSwift // // Created by Krunoslav Zaher on 10/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol SynchronizedUnsubscribeType : class { associatedtype DisposeKey func synchronizedUnsubscribe(_ disposeKey: DisposeKey) } ================================================ FILE: Pods/RxSwift/RxSwift/ConnectableObservableType.swift ================================================ // // ConnectableObservableType.swift // RxSwift // // Created by Krunoslav Zaher on 3/1/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /** Represents an observable sequence wrapper that can be connected and disconnected from its underlying observable sequence. */ public protocol ConnectableObservableType : ObservableType { /** Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established. - returns: Disposable used to disconnect the observable wrapper from its source, causing subscribed observer to stop receiving values from the underlying observable sequence. */ func connect() -> Disposable } ================================================ FILE: Pods/RxSwift/RxSwift/Deprecated.swift ================================================ // // Deprecated.swift // RxSwift // // Created by Krunoslav Zaher on 3/5/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // extension Observable { /** Converts a optional to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - parameter optional: Optional element in the resulting observable sequence. - returns: An observable sequence containing the wrapped value or not from given optional. */ @available(*, deprecated, message: "Implicit conversions from any type to optional type are allowed and that is causing issues with `from` operator overloading.", renamed: "from(optional:)") public static func from(_ optional: E?) -> Observable { return Observable.from(optional: optional) } /** Converts a optional to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - parameter optional: Optional element in the resulting observable sequence. - parameter: Scheduler to send the optional element on. - returns: An observable sequence containing the wrapped value or not from given optional. */ @available(*, deprecated, message: "Implicit conversions from any type to optional type are allowed and that is causing issues with `from` operator overloading.", renamed: "from(optional:scheduler:)") public static func from(_ optional: E?, scheduler: ImmediateSchedulerType) -> Observable { return Observable.from(optional: optional, scheduler: scheduler) } } extension ObservableType { /** Projects each element of an observable sequence into a new form by incorporating the element's index. - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html) - parameter selector: A transform function to apply to each source element; the second parameter of the function represents the index of the source element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ @available(*, deprecated, message: "Please use enumerated().map()") public func mapWithIndex(_ selector: @escaping (E, Int) throws -> R) -> Observable { return enumerated().map { try selector($0.element, $0.index) } } /** Projects each element of an observable sequence to an observable sequence by incorporating the element's index and merges the resulting observable sequences into one observable sequence. - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html) - parameter selector: A transform function to apply to each element; the second parameter of the function represents the index of the source element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ @available(*, deprecated, message: "Please use enumerated().flatMap()") public func flatMapWithIndex(_ selector: @escaping (E, Int) throws -> O) -> Observable { return enumerated().flatMap { try selector($0.element, $0.index) } } /** Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. The element's index is used in the logic of the predicate function. - seealso: [skipWhile operator on reactivex.io](http://reactivex.io/documentation/operators/skipwhile.html) - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element. - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. */ @available(*, deprecated, message: "Please use enumerated().skipWhile().map()") public func skipWhileWithIndex(_ predicate: @escaping (E, Int) throws -> Bool) -> Observable { return enumerated().skipWhile { try predicate($0.element, $0.index) }.map { $0.element } } /** Returns elements from an observable sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function. - seealso: [takeWhile operator on reactivex.io](http://reactivex.io/documentation/operators/takewhile.html) - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element. - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ @available(*, deprecated, message: "Please use enumerated().takeWhile().map()") public func takeWhileWithIndex(_ predicate: @escaping (E, Int) throws -> Bool) -> Observable { return enumerated().takeWhile { try predicate($0.element, $0.index) }.map { $0.element } } } extension Disposable { /// Deprecated in favor of `disposed(by:)` /// /// /// Adds `self` to `bag`. /// /// - parameter bag: `DisposeBag` to add `self` to. @available(*, deprecated, message: "use disposed(by:) instead", renamed: "disposed(by:)") public func addDisposableTo(_ bag: DisposeBag) { disposed(by: bag) } } extension ObservableType { /** Returns an observable sequence that shares a single subscription to the underlying sequence, and immediately upon subscription replays latest element in buffer. This operator is a specialization of replay which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed. - seealso: [shareReplay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html) - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence. */ @available(*, deprecated, message: "use share(replay: 1) instead", renamed: "share(replay:)") public func shareReplayLatestWhileConnected() -> Observable { return share(replay: 1, scope: .whileConnected) } } extension ObservableType { /** Returns an observable sequence that shares a single subscription to the underlying sequence, and immediately upon subscription replays maximum number of elements in buffer. This operator is a specialization of replay which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed. - seealso: [shareReplay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html) - parameter bufferSize: Maximum element count of the replay buffer. - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence. */ @available(*, deprecated, message: "Suggested replacement is `share(replay: 1)`. In case old 3.x behavior of `shareReplay` is required please use `share(replay: 1, scope: .forever)` instead.", renamed: "share(replay:)") public func shareReplay(_ bufferSize: Int) -> Observable { return self.share(replay: bufferSize, scope: .forever) } } /// Variable is a wrapper for `BehaviorSubject`. /// /// Unlike `BehaviorSubject` it can't terminate with error, and when variable is deallocated /// it will complete its observable sequence (`asObservable`). public final class Variable { public typealias E = Element private let _subject: BehaviorSubject private var _lock = SpinLock() // state private var _value: E #if DEBUG fileprivate let _synchronizationTracker = SynchronizationTracker() #endif /// Gets or sets current value of variable. /// /// Whenever a new value is set, all the observers are notified of the change. /// /// Even if the newly set value is same as the old value, observers are still notified for change. public var value: E { get { _lock.lock(); defer { _lock.unlock() } return _value } set(newValue) { #if DEBUG _synchronizationTracker.register(synchronizationErrorMessage: .variable) defer { _synchronizationTracker.unregister() } #endif _lock.lock() _value = newValue _lock.unlock() _subject.on(.next(newValue)) } } /// Initializes variable with initial value. /// /// - parameter value: Initial variable value. public init(_ value: Element) { _value = value _subject = BehaviorSubject(value: value) } /// - returns: Canonical interface for push style sequence public func asObservable() -> Observable { return _subject } deinit { _subject.on(.completed) } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposable.swift ================================================ // // Disposable.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Respresents a disposable resource. public protocol Disposable { /// Dispose resource. func dispose() } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/AnonymousDisposable.swift ================================================ // // AnonymousDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 2/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents an Action-based disposable. /// /// When dispose method is called, disposal action will be dereferenced. fileprivate final class AnonymousDisposable : DisposeBase, Cancelable { public typealias DisposeAction = () -> Void private var _isDisposed: AtomicInt = 0 private var _disposeAction: DisposeAction? /// - returns: Was resource disposed. public var isDisposed: Bool { return _isDisposed == 1 } /// Constructs a new disposable with the given action used for disposal. /// /// - parameter disposeAction: Disposal action which will be run upon calling `dispose`. fileprivate init(_ disposeAction: @escaping DisposeAction) { _disposeAction = disposeAction super.init() } // Non-deprecated version of the constructor, used by `Disposables.create(with:)` fileprivate init(disposeAction: @escaping DisposeAction) { _disposeAction = disposeAction super.init() } /// Calls the disposal action if and only if the current instance hasn't been disposed yet. /// /// After invoking disposal action, disposal action will be dereferenced. fileprivate func dispose() { if AtomicCompareAndSwap(0, 1, &_isDisposed) { assert(_isDisposed == 1) if let action = _disposeAction { _disposeAction = nil action() } } } } extension Disposables { /// Constructs a new disposable with the given action used for disposal. /// /// - parameter dispose: Disposal action which will be run upon calling `dispose`. public static func create(with dispose: @escaping () -> ()) -> Cancelable { return AnonymousDisposable(disposeAction: dispose) } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/BinaryDisposable.swift ================================================ // // BinaryDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 6/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents two disposable resources that are disposed together. private final class BinaryDisposable : DisposeBase, Cancelable { private var _isDisposed: AtomicInt = 0 // state private var _disposable1: Disposable? private var _disposable2: Disposable? /// - returns: Was resource disposed. var isDisposed: Bool { return _isDisposed > 0 } /// Constructs new binary disposable from two disposables. /// /// - parameter disposable1: First disposable /// - parameter disposable2: Second disposable init(_ disposable1: Disposable, _ disposable2: Disposable) { _disposable1 = disposable1 _disposable2 = disposable2 super.init() } /// Calls the disposal action if and only if the current instance hasn't been disposed yet. /// /// After invoking disposal action, disposal action will be dereferenced. func dispose() { if AtomicCompareAndSwap(0, 1, &_isDisposed) { _disposable1?.dispose() _disposable2?.dispose() _disposable1 = nil _disposable2 = nil } } } extension Disposables { /// Creates a disposable with the given disposables. public static func create(_ disposable1: Disposable, _ disposable2: Disposable) -> Cancelable { return BinaryDisposable(disposable1, disposable2) } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/BooleanDisposable.swift ================================================ // // BooleanDisposable.swift // RxSwift // // Created by Junior B. on 10/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents a disposable resource that can be checked for disposal status. public final class BooleanDisposable : Cancelable { internal static let BooleanDisposableTrue = BooleanDisposable(isDisposed: true) private var _isDisposed = false /// Initializes a new instance of the `BooleanDisposable` class public init() { } /// Initializes a new instance of the `BooleanDisposable` class with given value public init(isDisposed: Bool) { self._isDisposed = isDisposed } /// - returns: Was resource disposed. public var isDisposed: Bool { return _isDisposed } /// Sets the status to disposed, which can be observer through the `isDisposed` property. public func dispose() { _isDisposed = true } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/CompositeDisposable.swift ================================================ // // CompositeDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 2/20/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents a group of disposable resources that are disposed together. public final class CompositeDisposable : DisposeBase, Cancelable { /// Key used to remove disposable from composite disposable public struct DisposeKey { fileprivate let key: BagKey fileprivate init(key: BagKey) { self.key = key } } private var _lock = SpinLock() // state private var _disposables: Bag? = Bag() public var isDisposed: Bool { _lock.lock(); defer { _lock.unlock() } return _disposables == nil } public override init() { } /// Initializes a new instance of composite disposable with the specified number of disposables. public init(_ disposable1: Disposable, _ disposable2: Disposable) { // This overload is here to make sure we are using optimized version up to 4 arguments. let _ = _disposables!.insert(disposable1) let _ = _disposables!.insert(disposable2) } /// Initializes a new instance of composite disposable with the specified number of disposables. public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) { // This overload is here to make sure we are using optimized version up to 4 arguments. let _ = _disposables!.insert(disposable1) let _ = _disposables!.insert(disposable2) let _ = _disposables!.insert(disposable3) } /// Initializes a new instance of composite disposable with the specified number of disposables. public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposable4: Disposable, _ disposables: Disposable...) { // This overload is here to make sure we are using optimized version up to 4 arguments. let _ = _disposables!.insert(disposable1) let _ = _disposables!.insert(disposable2) let _ = _disposables!.insert(disposable3) let _ = _disposables!.insert(disposable4) for disposable in disposables { let _ = _disposables!.insert(disposable) } } /// Initializes a new instance of composite disposable with the specified number of disposables. public init(disposables: [Disposable]) { for disposable in disposables { let _ = _disposables!.insert(disposable) } } /** Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed. - parameter disposable: Disposable to add. - returns: Key that can be used to remove disposable from composite disposable. In case dispose bag was already disposed `nil` will be returned. */ public func insert(_ disposable: Disposable) -> DisposeKey? { let key = _insert(disposable) if key == nil { disposable.dispose() } return key } private func _insert(_ disposable: Disposable) -> DisposeKey? { _lock.lock(); defer { _lock.unlock() } let bagKey = _disposables?.insert(disposable) return bagKey.map(DisposeKey.init) } /// - returns: Gets the number of disposables contained in the `CompositeDisposable`. public var count: Int { _lock.lock(); defer { _lock.unlock() } return _disposables?.count ?? 0 } /// Removes and disposes the disposable identified by `disposeKey` from the CompositeDisposable. /// /// - parameter disposeKey: Key used to identify disposable to be removed. public func remove(for disposeKey: DisposeKey) { _remove(for: disposeKey)?.dispose() } private func _remove(for disposeKey: DisposeKey) -> Disposable? { _lock.lock(); defer { _lock.unlock() } return _disposables?.removeKey(disposeKey.key) } /// Disposes all disposables in the group and removes them from the group. public func dispose() { if let disposables = _dispose() { disposeAll(in: disposables) } } private func _dispose() -> Bag? { _lock.lock(); defer { _lock.unlock() } let disposeBag = _disposables _disposables = nil return disposeBag } } extension Disposables { /// Creates a disposable with the given disposables. public static func create(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) -> Cancelable { return CompositeDisposable(disposable1, disposable2, disposable3) } /// Creates a disposable with the given disposables. public static func create(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposables: Disposable ...) -> Cancelable { var disposables = disposables disposables.append(disposable1) disposables.append(disposable2) disposables.append(disposable3) return CompositeDisposable(disposables: disposables) } /// Creates a disposable with the given disposables. public static func create(_ disposables: [Disposable]) -> Cancelable { switch disposables.count { case 2: return Disposables.create(disposables[0], disposables[1]) default: return CompositeDisposable(disposables: disposables) } } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/Disposables.swift ================================================ // // Disposables.swift // RxSwift // // Created by Mohsen Ramezanpoor on 01/08/2016. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // /// A collection of utility methods for common disposable operations. public struct Disposables { private init() {} } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/DisposeBag.swift ================================================ // // DisposeBag.swift // RxSwift // // Created by Krunoslav Zaher on 3/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension Disposable { /// Adds `self` to `bag` /// /// - parameter bag: `DisposeBag` to add `self` to. public func disposed(by bag: DisposeBag) { bag.insert(self) } } /** Thread safe bag that disposes added disposables on `deinit`. This returns ARC (RAII) like resource management to `RxSwift`. In case contained disposables need to be disposed, just put a different dispose bag or create a new one in its place. self.existingDisposeBag = DisposeBag() In case explicit disposal is necessary, there is also `CompositeDisposable`. */ public final class DisposeBag: DisposeBase { private var _lock = SpinLock() // state private var _disposables = [Disposable]() private var _isDisposed = false /// Constructs new empty dispose bag. public override init() { super.init() } /// Adds `disposable` to be disposed when dispose bag is being deinited. /// /// - parameter disposable: Disposable to add. public func insert(_ disposable: Disposable) { _insert(disposable)?.dispose() } private func _insert(_ disposable: Disposable) -> Disposable? { _lock.lock(); defer { _lock.unlock() } if _isDisposed { return disposable } _disposables.append(disposable) return nil } /// This is internal on purpose, take a look at `CompositeDisposable` instead. private func dispose() { let oldDisposables = _dispose() for disposable in oldDisposables { disposable.dispose() } } private func _dispose() -> [Disposable] { _lock.lock(); defer { _lock.unlock() } let disposables = _disposables _disposables.removeAll(keepingCapacity: false) _isDisposed = true return disposables } deinit { dispose() } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/DisposeBase.swift ================================================ // // DisposeBase.swift // RxSwift // // Created by Krunoslav Zaher on 4/4/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Base class for all disposables. public class DisposeBase { init() { #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif } deinit { #if TRACE_RESOURCES let _ = Resources.decrementTotal() #endif } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/NopDisposable.swift ================================================ // // NopDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 2/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents a disposable that does nothing on disposal. /// /// Nop = No Operation fileprivate struct NopDisposable : Disposable { fileprivate static let noOp: Disposable = NopDisposable() fileprivate init() { } /// Does nothing. public func dispose() { } } extension Disposables { /** Creates a disposable that does nothing on disposal. */ static public func create() -> Disposable { return NopDisposable.noOp } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/RefCountDisposable.swift ================================================ // // RefCountDisposable.swift // RxSwift // // Created by Junior B. on 10/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed. public final class RefCountDisposable : DisposeBase, Cancelable { private var _lock = SpinLock() private var _disposable = nil as Disposable? private var _primaryDisposed = false private var _count = 0 /// - returns: Was resource disposed. public var isDisposed: Bool { _lock.lock(); defer { _lock.unlock() } return _disposable == nil } /// Initializes a new instance of the `RefCountDisposable`. public init(disposable: Disposable) { _disposable = disposable super.init() } /** Holds a dependent disposable that when disposed decreases the refcount on the underlying disposable. When getter is called, a dependent disposable contributing to the reference count that manages the underlying disposable's lifetime is returned. */ public func retain() -> Disposable { return _lock.calculateLocked { if let _ = _disposable { do { let _ = try incrementChecked(&_count) } catch (_) { rxFatalError("RefCountDisposable increment failed") } return RefCountInnerDisposable(self) } else { return Disposables.create() } } } /// Disposes the underlying disposable only when all dependent disposables have been disposed. public func dispose() { let oldDisposable: Disposable? = _lock.calculateLocked { if let oldDisposable = _disposable, !_primaryDisposed { _primaryDisposed = true if (_count == 0) { _disposable = nil return oldDisposable } } return nil } if let disposable = oldDisposable { disposable.dispose() } } fileprivate func release() { let oldDisposable: Disposable? = _lock.calculateLocked { if let oldDisposable = _disposable { do { let _ = try decrementChecked(&_count) } catch (_) { rxFatalError("RefCountDisposable decrement on release failed") } guard _count >= 0 else { rxFatalError("RefCountDisposable counter is lower than 0") } if _primaryDisposed && _count == 0 { _disposable = nil return oldDisposable } } return nil } if let disposable = oldDisposable { disposable.dispose() } } } internal final class RefCountInnerDisposable: DisposeBase, Disposable { private let _parent: RefCountDisposable private var _isDisposed: AtomicInt = 0 init(_ parent: RefCountDisposable) { _parent = parent super.init() } internal func dispose() { if AtomicCompareAndSwap(0, 1, &_isDisposed) { _parent.release() } } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/ScheduledDisposable.swift ================================================ // // ScheduledDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 6/13/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // private let disposeScheduledDisposable: (ScheduledDisposable) -> Disposable = { sd in sd.disposeInner() return Disposables.create() } /// Represents a disposable resource whose disposal invocation will be scheduled on the specified scheduler. public final class ScheduledDisposable : Cancelable { public let scheduler: ImmediateSchedulerType private var _isDisposed: AtomicInt = 0 // state private var _disposable: Disposable? /// - returns: Was resource disposed. public var isDisposed: Bool { return _isDisposed == 1 } /** Initializes a new instance of the `ScheduledDisposable` that uses a `scheduler` on which to dispose the `disposable`. - parameter scheduler: Scheduler where the disposable resource will be disposed on. - parameter disposable: Disposable resource to dispose on the given scheduler. */ public init(scheduler: ImmediateSchedulerType, disposable: Disposable) { self.scheduler = scheduler _disposable = disposable } /// Disposes the wrapped disposable on the provided scheduler. public func dispose() { let _ = scheduler.schedule(self, action: disposeScheduledDisposable) } func disposeInner() { if AtomicCompareAndSwap(0, 1, &_isDisposed) { _disposable!.dispose() _disposable = nil } } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/SerialDisposable.swift ================================================ // // SerialDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 3/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource. public final class SerialDisposable : DisposeBase, Cancelable { private var _lock = SpinLock() // state private var _current = nil as Disposable? private var _isDisposed = false /// - returns: Was resource disposed. public var isDisposed: Bool { return _isDisposed } /// Initializes a new instance of the `SerialDisposable`. override public init() { super.init() } /** Gets or sets the underlying disposable. Assigning this property disposes the previous disposable object. If the `SerialDisposable` has already been disposed, assignment to this property causes immediate disposal of the given disposable object. */ public var disposable: Disposable { get { return _lock.calculateLocked { return self.disposable } } set (newDisposable) { let disposable: Disposable? = _lock.calculateLocked { if _isDisposed { return newDisposable } else { let toDispose = _current _current = newDisposable return toDispose } } if let disposable = disposable { disposable.dispose() } } } /// Disposes the underlying disposable as well as all future replacements. public func dispose() { _dispose()?.dispose() } private func _dispose() -> Disposable? { _lock.lock(); defer { _lock.unlock() } if _isDisposed { return nil } else { _isDisposed = true let current = _current _current = nil return current } } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/SingleAssignmentDisposable.swift ================================================ // // SingleAssignmentDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 2/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /** Represents a disposable resource which only allows a single assignment of its underlying disposable resource. If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an exception. */ public final class SingleAssignmentDisposable : DisposeBase, Cancelable { fileprivate enum DisposeState: UInt32 { case disposed = 1 case disposableSet = 2 } // Jeej, swift API consistency rules fileprivate enum DisposeStateInt32: Int32 { case disposed = 1 case disposableSet = 2 } // state private var _state: AtomicInt = 0 private var _disposable = nil as Disposable? /// - returns: A value that indicates whether the object is disposed. public var isDisposed: Bool { return AtomicFlagSet(DisposeState.disposed.rawValue, &_state) } /// Initializes a new instance of the `SingleAssignmentDisposable`. public override init() { super.init() } /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined. /// /// **Throws exception if the `SingleAssignmentDisposable` has already been assigned to.** public func setDisposable(_ disposable: Disposable) { _disposable = disposable let previousState = AtomicOr(DisposeState.disposableSet.rawValue, &_state) if (previousState & DisposeStateInt32.disposableSet.rawValue) != 0 { rxFatalError("oldState.disposable != nil") } if (previousState & DisposeStateInt32.disposed.rawValue) != 0 { disposable.dispose() _disposable = nil } } /// Disposes the underlying disposable. public func dispose() { let previousState = AtomicOr(DisposeState.disposed.rawValue, &_state) if (previousState & DisposeStateInt32.disposed.rawValue) != 0 { return } if (previousState & DisposeStateInt32.disposableSet.rawValue) != 0 { guard let disposable = _disposable else { rxFatalError("Disposable not set") } disposable.dispose() _disposable = nil } } } ================================================ FILE: Pods/RxSwift/RxSwift/Disposables/SubscriptionDisposable.swift ================================================ // // SubscriptionDisposable.swift // RxSwift // // Created by Krunoslav Zaher on 10/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // struct SubscriptionDisposable : Disposable { private let _key: T.DisposeKey private weak var _owner: T? init(owner: T, key: T.DisposeKey) { _owner = owner _key = key } func dispose() { _owner?.synchronizedUnsubscribe(_key) } } ================================================ FILE: Pods/RxSwift/RxSwift/Errors.swift ================================================ // // Errors.swift // RxSwift // // Created by Krunoslav Zaher on 3/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // let RxErrorDomain = "RxErrorDomain" let RxCompositeFailures = "RxCompositeFailures" /// Generic Rx error codes. public enum RxError : Swift.Error , CustomDebugStringConvertible { /// Unknown error occurred. case unknown /// Performing an action on disposed object. case disposed(object: AnyObject) /// Aritmetic overflow error. case overflow /// Argument out of range error. case argumentOutOfRange /// Sequence doesn't contain any elements. case noElements /// Sequence contains more than one element. case moreThanOneElement /// Timeout error. case timeout } extension RxError { /// A textual representation of `self`, suitable for debugging. public var debugDescription: String { switch self { case .unknown: return "Unknown error occurred." case .disposed(let object): return "Object `\(object)` was already disposed." case .overflow: return "Arithmetic overflow occurred." case .argumentOutOfRange: return "Argument out of range." case .noElements: return "Sequence doesn't contain any elements." case .moreThanOneElement: return "Sequence contains more than one element." case .timeout: return "Sequence timeout." } } } ================================================ FILE: Pods/RxSwift/RxSwift/Event.swift ================================================ // // Event.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents a sequence event. /// /// Sequence grammar: /// **next\* (error | completed)** public enum Event { /// Next element is produced. case next(Element) /// Sequence terminated with an error. case error(Swift.Error) /// Sequence completed successfully. case completed } extension Event : CustomDebugStringConvertible { /// - returns: Description of event. public var debugDescription: String { switch self { case .next(let value): return "next(\(value))" case .error(let error): return "error(\(error))" case .completed: return "completed" } } } extension Event { /// Is `completed` or `error` event. public var isStopEvent: Bool { switch self { case .next: return false case .error, .completed: return true } } /// If `next` event, returns element value. public var element: Element? { if case .next(let value) = self { return value } return nil } /// If `error` event, returns error. public var error: Swift.Error? { if case .error(let error) = self { return error } return nil } /// If `completed` event, returns true. public var isCompleted: Bool { if case .completed = self { return true } return false } } extension Event { /// Maps sequence elements using transform. If error happens during the transform .error /// will be returned as value public func map(_ transform: (Element) throws -> Result) -> Event { do { switch self { case let .next(element): return .next(try transform(element)) case let .error(error): return .error(error) case .completed: return .completed } } catch let e { return .error(e) } } } /// A type that can be converted to `Event`. public protocol EventConvertible { /// Type of element in event associatedtype ElementType /// Event representation of this instance var event: Event { get } } extension Event : EventConvertible { /// Event representation of this instance public var event: Event { return self } } ================================================ FILE: Pods/RxSwift/RxSwift/Extensions/Bag+Rx.swift ================================================ // // Bag+Rx.swift // RxSwift // // Created by Krunoslav Zaher on 10/19/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // // MARK: forEach @inline(__always) func dispatch(_ bag: Bag<(Event) -> ()>, _ event: Event) { if bag._onlyFastPath { bag._value0?(event) return } let value0 = bag._value0 let dictionary = bag._dictionary if let value0 = value0 { value0(event) } let pairs = bag._pairs for i in 0 ..< pairs.count { pairs[i].value(event) } if let dictionary = dictionary { for element in dictionary.values { element(event) } } } /// Dispatches `dispose` to all disposables contained inside bag. func disposeAll(in bag: Bag) { if bag._onlyFastPath { bag._value0?.dispose() return } let value0 = bag._value0 let dictionary = bag._dictionary if let value0 = value0 { value0.dispose() } let pairs = bag._pairs for i in 0 ..< pairs.count { pairs[i].value.dispose() } if let dictionary = dictionary { for element in dictionary.values { element.dispose() } } } ================================================ FILE: Pods/RxSwift/RxSwift/Extensions/String+Rx.swift ================================================ // // String+Rx.swift // RxSwift // // Created by Krunoslav Zaher on 12/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension String { /// This is needed because on Linux Swift doesn't have `rangeOfString(..., options: .BackwardsSearch)` func lastIndexOf(_ character: Character) -> Index? { var index = endIndex while index > startIndex { index = self.index(before: index) if self[index] == character { return index } } return nil } } ================================================ FILE: Pods/RxSwift/RxSwift/GroupedObservable.swift ================================================ // // GroupedObservable.swift // RxSwift // // Created by Tomi Koskinen on 01/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents an observable sequence of elements that have a common key. public struct GroupedObservable : ObservableType { public typealias E = Element /// Gets the common key. public let key: Key private let source: Observable /// Initializes grouped observable sequence with key and source observable sequence. /// /// - parameter key: Grouped observable sequence key /// - parameter source: Observable sequence that represents sequence of elements for the key /// - returns: Grouped observable sequence of elements for the specific key public init(key: Key, source: Observable) { self.key = key self.source = source } /// Subscribes `observer` to receive events for this sequence. public func subscribe(_ observer: O) -> Disposable where O.E == E { return self.source.subscribe(observer) } /// Converts `self` to `Observable` sequence. public func asObservable() -> Observable { return source } } ================================================ FILE: Pods/RxSwift/RxSwift/ImmediateSchedulerType.swift ================================================ // // ImmediateSchedulerType.swift // RxSwift // // Created by Krunoslav Zaher on 5/31/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents an object that immediately schedules units of work. public protocol ImmediateSchedulerType { /** Schedules an action to be executed immediately. - parameter state: State passed to the action to be executed. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable } extension ImmediateSchedulerType { /** Schedules an action to be executed recursively. - parameter state: State passed to the action to be executed. - parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func scheduleRecursive(_ state: State, action: @escaping (_ state: State, _ recurse: (State) -> ()) -> ()) -> Disposable { let recursiveScheduler = RecursiveImmediateScheduler(action: action, scheduler: self) recursiveScheduler.schedule(state) return Disposables.create(with: recursiveScheduler.dispose) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observable.swift ================================================ // // Observable.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// A type-erased `ObservableType`. /// /// It represents a push style sequence. public class Observable : ObservableType { /// Type of elements in sequence. public typealias E = Element init() { #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif } public func subscribe(_ observer: O) -> Disposable where O.E == E { rxAbstractMethod() } public func asObservable() -> Observable { return self } deinit { #if TRACE_RESOURCES let _ = Resources.decrementTotal() #endif } // this is kind of ugly I know :( // Swift compiler reports "Not supported yet" when trying to override protocol extensions, so ¯\_(ツ)_/¯ /// Optimizations for map operator internal func composeMap(_ transform: @escaping (Element) throws -> R) -> Observable { return _map(source: self, transform: transform) } } ================================================ FILE: Pods/RxSwift/RxSwift/ObservableConvertibleType.swift ================================================ // // ObservableConvertibleType.swift // RxSwift // // Created by Krunoslav Zaher on 9/17/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Type that can be converted to observable sequence (`Observable`). public protocol ObservableConvertibleType { /// Type of elements in sequence. associatedtype E /// Converts `self` to `Observable` sequence. /// /// - returns: Observable sequence that represents `self`. func asObservable() -> Observable } ================================================ FILE: Pods/RxSwift/RxSwift/ObservableType+Extensions.swift ================================================ // // ObservableType+Extensions.swift // RxSwift // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if DEBUG import Foundation #endif extension ObservableType { /** Subscribes an event handler to an observable sequence. - parameter on: Action to invoke for each event in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ public func subscribe(_ on: @escaping (Event) -> Void) -> Disposable { let observer = AnonymousObserver { e in on(e) } return self.asObservable().subscribe(observer) } /** Subscribes an element handler, an error handler, a completion handler and disposed handler to an observable sequence. - parameter onNext: Action to invoke for each element in the observable sequence. - parameter onError: Action to invoke upon errored termination of the observable sequence. - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has gracefully completed, errored, or if the generation is canceled by disposing subscription). - returns: Subscription object used to unsubscribe from the observable sequence. */ public func subscribe(onNext: ((E) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { #if DEBUG let disposable: Disposable if let disposed = onDisposed { disposable = Disposables.create(with: disposed) } else { disposable = Disposables.create() } let synchronizationTracker = SynchronizationTracker() let callStack = Thread.callStackSymbols let observer = AnonymousObserver { event in synchronizationTracker.register(synchronizationErrorMessage: .default) defer { synchronizationTracker.unregister() } switch event { case .next(let value): onNext?(value) case .error(let error): if let onError = onError { onError(error) } else { Hooks.defaultErrorHandler(callStack, error) } disposable.dispose() case .completed: onCompleted?() disposable.dispose() } } return Disposables.create( self.asObservable().subscribe(observer), disposable ) #else let disposable: Disposable if let disposed = onDisposed { disposable = Disposables.create(with: disposed) } else { disposable = Disposables.create() } let observer = AnonymousObserver { event in switch event { case .next(let value): onNext?(value) case .error(let error): if let onError = onError { onError(error) } else { Hooks.defaultErrorHandler([], error) } disposable.dispose() case .completed: onCompleted?() disposable.dispose() } } return Disposables.create( self.asObservable().subscribe(observer), disposable ) #endif } } import class Foundation.NSRecursiveLock extension Hooks { public typealias DefaultErrorHandler = (_ subscriptionCallStack: [String], _ error: Error) -> () fileprivate static let _lock = RecursiveLock() fileprivate static var _defaultErrorHandler: DefaultErrorHandler = { subscriptionCallStack, error in #if DEBUG let serializedCallStack = subscriptionCallStack.joined(separator: "\n") print("Unhandled error happened: \(error)\n subscription called from:\n\(serializedCallStack)") #endif } /// Error handler called in case onError handler wasn't provided. public static var defaultErrorHandler: DefaultErrorHandler { get { _lock.lock(); defer { _lock.unlock() } return _defaultErrorHandler } set { _lock.lock(); defer { _lock.unlock() } _defaultErrorHandler = newValue } } } ================================================ FILE: Pods/RxSwift/RxSwift/ObservableType.swift ================================================ // // ObservableType.swift // RxSwift // // Created by Krunoslav Zaher on 8/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Represents a push style sequence. public protocol ObservableType : ObservableConvertibleType { /** Subscribes `observer` to receive events for this sequence. ### Grammar **Next\* (Error | Completed)?** * sequences can produce zero or more elements so zero or more `Next` events can be sent to `observer` * once an `Error` or `Completed` event is sent, the sequence terminates and can't produce any other elements It is possible that events are sent from different threads, but no two events can be sent concurrently to `observer`. ### Resource Management When sequence sends `Complete` or `Error` event all internal resources that compute sequence elements will be freed. To cancel production of sequence elements and free resources immediately, call `dispose` on returned subscription. - returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources. */ func subscribe(_ observer: O) -> Disposable where O.E == E } extension ObservableType { /// Default implementation of converting `ObservableType` to `Observable`. public func asObservable() -> Observable { // temporary workaround //return Observable.create(subscribe: self.subscribe) return Observable.create { o in return self.subscribe(o) } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/AddRef.swift ================================================ // // AddRef.swift // RxSwift // // Created by Junior B. on 30/10/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // final class AddRefSink : Sink, ObserverType { typealias Element = O.E override init(observer: O, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(_): forwardOn(event) case .completed, .error(_): forwardOn(event) dispose() } } } final class AddRef : Producer { typealias EventHandler = (Event) throws -> Void private let _source: Observable private let _refCount: RefCountDisposable init(source: Observable, refCount: RefCountDisposable) { _source = source _refCount = refCount } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let releaseDisposable = _refCount.retain() let sink = AddRefSink(observer: observer, cancel: cancel) let subscription = Disposables.create(releaseDisposable, _source.subscribe(sink)) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Amb.swift ================================================ // // Amb.swift // RxSwift // // Created by Krunoslav Zaher on 6/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Propagates the observable sequence that reacts first. - seealso: [amb operator on reactivex.io](http://reactivex.io/documentation/operators/amb.html) - returns: An observable sequence that surfaces any of the given sequences, whichever reacted first. */ public static func amb(_ sequence: S) -> Observable where S.Iterator.Element == Observable { return sequence.reduce(Observable.never()) { a, o in return a.amb(o.asObservable()) } } } extension ObservableType { /** Propagates the observable sequence that reacts first. - seealso: [amb operator on reactivex.io](http://reactivex.io/documentation/operators/amb.html) - parameter right: Second observable sequence. - returns: An observable sequence that surfaces either of the given sequences, whichever reacted first. */ public func amb (_ right: O2) -> Observable where O2.E == E { return Amb(left: asObservable(), right: right.asObservable()) } } fileprivate enum AmbState { case neither case left case right } final fileprivate class AmbObserver : ObserverType { typealias Element = O.E typealias Parent = AmbSink typealias This = AmbObserver typealias Sink = (This, Event) -> Void fileprivate let _parent: Parent fileprivate var _sink: Sink fileprivate var _cancel: Disposable init(parent: Parent, cancel: Disposable, sink: @escaping Sink) { #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif _parent = parent _sink = sink _cancel = cancel } func on(_ event: Event) { _sink(self, event) if event.isStopEvent { _cancel.dispose() } } deinit { #if TRACE_RESOURCES let _ = Resources.decrementTotal() #endif } } final fileprivate class AmbSink : Sink { typealias ElementType = O.E typealias Parent = Amb typealias AmbObserverType = AmbObserver private let _parent: Parent private let _lock = RecursiveLock() // state private var _choice = AmbState.neither init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let disposeAll = Disposables.create(subscription1, subscription2) let forwardEvent = { (o: AmbObserverType, event: Event) -> Void in self.forwardOn(event) if event.isStopEvent { self.dispose() } } let decide = { (o: AmbObserverType, event: Event, me: AmbState, otherSubscription: Disposable) in self._lock.performLocked { if self._choice == .neither { self._choice = me o._sink = forwardEvent o._cancel = disposeAll otherSubscription.dispose() } if self._choice == me { self.forwardOn(event) if event.isStopEvent { self.dispose() } } } } let sink1 = AmbObserver(parent: self, cancel: subscription1) { o, e in decide(o, e, .left, subscription2) } let sink2 = AmbObserver(parent: self, cancel: subscription1) { o, e in decide(o, e, .right, subscription1) } subscription1.setDisposable(_parent._left.subscribe(sink1)) subscription2.setDisposable(_parent._right.subscribe(sink2)) return disposeAll } } final fileprivate class Amb: Producer { fileprivate let _left: Observable fileprivate let _right: Observable init(left: Observable, right: Observable) { _left = left _right = right } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = AmbSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/AsMaybe.swift ================================================ // // AsMaybe.swift // RxSwift // // Created by Krunoslav Zaher on 3/12/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // fileprivate final class AsMaybeSink : Sink, ObserverType { typealias ElementType = O.E typealias E = ElementType private var _element: Event? = nil func on(_ event: Event) { switch event { case .next: if _element != nil { forwardOn(.error(RxError.moreThanOneElement)) dispose() } _element = event case .error: forwardOn(event) dispose() case .completed: if let element = _element { forwardOn(element) } forwardOn(.completed) dispose() } } } final class AsMaybe: Producer { fileprivate let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = AsMaybeSink(observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/AsSingle.swift ================================================ // // AsSingle.swift // RxSwift // // Created by Krunoslav Zaher on 3/12/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // fileprivate final class AsSingleSink : Sink, ObserverType { typealias ElementType = O.E typealias E = ElementType private var _element: Event? = nil func on(_ event: Event) { switch event { case .next: if _element != nil { forwardOn(.error(RxError.moreThanOneElement)) dispose() } _element = event case .error: forwardOn(event) dispose() case .completed: if let element = _element { forwardOn(element) forwardOn(.completed) } else { forwardOn(.error(RxError.noElements)) } dispose() } } } final class AsSingle: Producer { fileprivate let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = AsSingleSink(observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Buffer.swift ================================================ // // Buffer.swift // RxSwift // // Created by Krunoslav Zaher on 9/13/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Projects each element of an observable sequence into a buffer that's sent out when either it's full or a given amount of time has elapsed, using the specified scheduler to run timers. A useful real-world analogy of this overload is the behavior of a ferry leaving the dock when all seats are taken, or at the scheduled time of departure, whichever event occurs first. - seealso: [buffer operator on reactivex.io](http://reactivex.io/documentation/operators/buffer.html) - parameter timeSpan: Maximum time length of a buffer. - parameter count: Maximum element count of a buffer. - parameter scheduler: Scheduler to run buffering timers on. - returns: An observable sequence of buffers. */ public func buffer(timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType) -> Observable<[E]> { return BufferTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler) } } final fileprivate class BufferTimeCount : Producer<[Element]> { fileprivate let _timeSpan: RxTimeInterval fileprivate let _count: Int fileprivate let _scheduler: SchedulerType fileprivate let _source: Observable init(source: Observable, timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType) { _source = source _timeSpan = timeSpan _count = count _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == [Element] { let sink = BufferTimeCountSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } final fileprivate class BufferTimeCountSink : Sink , LockOwnerType , ObserverType , SynchronizedOnType where O.E == [Element] { typealias Parent = BufferTimeCount typealias E = Element private let _parent: Parent let _lock = RecursiveLock() // state private let _timerD = SerialDisposable() private var _buffer = [Element]() private var _windowID = 0 init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { createTimer(_windowID) return Disposables.create(_timerD, _parent._source.subscribe(self)) } func startNewWindowAndSendCurrentOne() { _windowID = _windowID &+ 1 let windowID = _windowID let buffer = _buffer _buffer = [] forwardOn(.next(buffer)) createTimer(windowID) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next(let element): _buffer.append(element) if _buffer.count == _parent._count { startNewWindowAndSendCurrentOne() } case .error(let error): _buffer = [] forwardOn(.error(error)) dispose() case .completed: forwardOn(.next(_buffer)) forwardOn(.completed) dispose() } } func createTimer(_ windowID: Int) { if _timerD.isDisposed { return } if _windowID != windowID { return } let nextTimer = SingleAssignmentDisposable() _timerD.disposable = nextTimer let disposable = _parent._scheduler.scheduleRelative(windowID, dueTime: _parent._timeSpan) { previousWindowID in self._lock.performLocked { if previousWindowID != self._windowID { return } self.startNewWindowAndSendCurrentOne() } return Disposables.create() } nextTimer.setDisposable(disposable) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Catch.swift ================================================ // // Catch.swift // RxSwift // // Created by Krunoslav Zaher on 4/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Continues an observable sequence that is terminated by an error with the observable sequence produced by the handler. - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html) - parameter handler: Error handler function, producing another observable sequence. - returns: An observable sequence containing the source sequence's elements, followed by the elements produced by the handler's resulting observable sequence in case an error occurred. */ public func catchError(_ handler: @escaping (Swift.Error) throws -> Observable) -> Observable { return Catch(source: asObservable(), handler: handler) } /** Continues an observable sequence that is terminated by an error with a single element. - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html) - parameter element: Last element in an observable sequence in case error occurs. - returns: An observable sequence containing the source sequence's elements, followed by the `element` in case an error occurred. */ public func catchErrorJustReturn(_ element: E) -> Observable { return Catch(source: asObservable(), handler: { _ in Observable.just(element) }) } } extension ObservableType { /** Continues an observable sequence that is terminated by an error with the next observable sequence. - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html) - returns: An observable sequence containing elements from consecutive source sequences until a source sequence terminates successfully. */ public static func catchError(_ sequence: S) -> Observable where S.Iterator.Element == Observable { return CatchSequence(sources: sequence) } } extension ObservableType { /** Repeats the source observable sequence until it successfully terminates. **This could potentially create an infinite sequence.** - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html) - returns: Observable sequence to repeat until it successfully terminates. */ public func retry() -> Observable { return CatchSequence(sources: InfiniteSequence(repeatedValue: self.asObservable())) } /** Repeats the source observable sequence the specified number of times in case of an error or until it successfully terminates. If you encounter an error and want it to retry once, then you must use `retry(2)` - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html) - parameter maxAttemptCount: Maximum number of times to repeat the sequence. - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully. */ public func retry(_ maxAttemptCount: Int) -> Observable { return CatchSequence(sources: Swift.repeatElement(self.asObservable(), count: maxAttemptCount)) } } // catch with callback final fileprivate class CatchSinkProxy : ObserverType { typealias E = O.E typealias Parent = CatchSink private let _parent: Parent init(parent: Parent) { _parent = parent } func on(_ event: Event) { _parent.forwardOn(event) switch event { case .next: break case .error, .completed: _parent.dispose() } } } final fileprivate class CatchSink : Sink, ObserverType { typealias E = O.E typealias Parent = Catch private let _parent: Parent private let _subscription = SerialDisposable() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let d1 = SingleAssignmentDisposable() _subscription.disposable = d1 d1.setDisposable(_parent._source.subscribe(self)) return _subscription } func on(_ event: Event) { switch event { case .next: forwardOn(event) case .completed: forwardOn(event) dispose() case .error(let error): do { let catchSequence = try _parent._handler(error) let observer = CatchSinkProxy(parent: self) _subscription.disposable = catchSequence.subscribe(observer) } catch let e { forwardOn(.error(e)) dispose() } } } } final fileprivate class Catch : Producer { typealias Handler = (Swift.Error) throws -> Observable fileprivate let _source: Observable fileprivate let _handler: Handler init(source: Observable, handler: @escaping Handler) { _source = source _handler = handler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = CatchSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // catch enumerable final fileprivate class CatchSequenceSink : TailRecursiveSink , ObserverType where S.Iterator.Element : ObservableConvertibleType, S.Iterator.Element.E == O.E { typealias Element = O.E typealias Parent = CatchSequence private var _lastError: Swift.Error? override init(observer: O, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next: forwardOn(event) case .error(let error): _lastError = error schedule(.moveNext) case .completed: forwardOn(event) dispose() } } override func subscribeToNext(_ source: Observable) -> Disposable { return source.subscribe(self) } override func done() { if let lastError = _lastError { forwardOn(.error(lastError)) } else { forwardOn(.completed) } self.dispose() } override func extract(_ observable: Observable) -> SequenceGenerator? { if let onError = observable as? CatchSequence { return (onError.sources.makeIterator(), nil) } else { return nil } } } final fileprivate class CatchSequence : Producer where S.Iterator.Element : ObservableConvertibleType { typealias Element = S.Iterator.Element.E let sources: S init(sources: S) { self.sources = sources } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = CatchSequenceSink(observer: observer, cancel: cancel) let subscription = sink.run((self.sources.makeIterator(), nil)) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/CombineLatest+Collection.swift ================================================ // // CombineLatest+Collection.swift // RxSwift // // Created by Krunoslav Zaher on 8/29/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combinelatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest(_ collection: C, _ resultSelector: @escaping ([C.Iterator.Element.E]) throws -> E) -> Observable where C.Iterator.Element: ObservableType { return CombineLatestCollectionType(sources: collection, resultSelector: resultSelector) } /** Merges the specified observable sequences into one observable sequence whenever any of the observable sequences produces an element. - seealso: [combinelatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest(_ collection: C) -> Observable<[E]> where C.Iterator.Element: ObservableType, C.Iterator.Element.E == E { return CombineLatestCollectionType(sources: collection, resultSelector: { $0 }) } } final fileprivate class CombineLatestCollectionTypeSink : Sink where C.Iterator.Element : ObservableConvertibleType { typealias R = O.E typealias Parent = CombineLatestCollectionType typealias SourceElement = C.Iterator.Element.E let _parent: Parent let _lock = RecursiveLock() // state var _numberOfValues = 0 var _values: [SourceElement?] var _isDone: [Bool] var _numberOfDone = 0 var _subscriptions: [SingleAssignmentDisposable] init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _values = [SourceElement?](repeating: nil, count: parent._count) _isDone = [Bool](repeating: false, count: parent._count) _subscriptions = Array() _subscriptions.reserveCapacity(parent._count) for _ in 0 ..< parent._count { _subscriptions.append(SingleAssignmentDisposable()) } super.init(observer: observer, cancel: cancel) } func on(_ event: Event, atIndex: Int) { _lock.lock(); defer { _lock.unlock() } // { switch event { case .next(let element): if _values[atIndex] == nil { _numberOfValues += 1 } _values[atIndex] = element if _numberOfValues < _parent._count { let numberOfOthersThatAreDone = self._numberOfDone - (_isDone[atIndex] ? 1 : 0) if numberOfOthersThatAreDone == self._parent._count - 1 { forwardOn(.completed) dispose() } return } do { let result = try _parent._resultSelector(_values.map { $0! }) forwardOn(.next(result)) } catch let error { forwardOn(.error(error)) dispose() } case .error(let error): forwardOn(.error(error)) dispose() case .completed: if _isDone[atIndex] { return } _isDone[atIndex] = true _numberOfDone += 1 if _numberOfDone == self._parent._count { forwardOn(.completed) dispose() } else { _subscriptions[atIndex].dispose() } } // } } func run() -> Disposable { var j = 0 for i in _parent._sources { let index = j let source = i.asObservable() let disposable = source.subscribe(AnyObserver { event in self.on(event, atIndex: index) }) _subscriptions[j].setDisposable(disposable) j += 1 } if _parent._sources.isEmpty { self.forwardOn(.completed) } return Disposables.create(_subscriptions) } } final fileprivate class CombineLatestCollectionType : Producer where C.Iterator.Element : ObservableConvertibleType { typealias ResultSelector = ([C.Iterator.Element.E]) throws -> R let _sources: C let _resultSelector: ResultSelector let _count: Int init(sources: C, resultSelector: @escaping ResultSelector) { _sources = sources _resultSelector = resultSelector _count = Int(Int64(self._sources.count)) } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestCollectionTypeSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/CombineLatest+arity.swift ================================================ // This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project // // CombineLatest+arity.swift // RxSwift // // Created by Krunoslav Zaher on 4/22/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // // 2 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.E, O2.E) throws -> E) -> Observable { return CombineLatest2( source1: source1.asObservable(), source2: source2.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2) -> Observable<(O1.E, O2.E)> { return CombineLatest2( source1: source1.asObservable(), source2: source2.asObservable(), resultSelector: { ($0, $1) } ) } } final class CombineLatestSink2_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest2 let _parent: Parent var _latestElement1: E1! = nil var _latestElement2: E2! = nil init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 2, observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) subscription1.setDisposable(_parent._source1.subscribe(observer1)) subscription2.setDisposable(_parent._source2.subscribe(observer2)) return Disposables.create([ subscription1, subscription2 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_latestElement1, _latestElement2) } } final class CombineLatest2 : Producer { typealias ResultSelector = (E1, E2) throws -> R let _source1: Observable let _source2: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, resultSelector: @escaping ResultSelector) { _source1 = source1 _source2 = source2 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestSink2_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 3 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.E, O2.E, O3.E) throws -> E) -> Observable { return CombineLatest3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3) -> Observable<(O1.E, O2.E, O3.E)> { return CombineLatest3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), resultSelector: { ($0, $1, $2) } ) } } final class CombineLatestSink3_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest3 let _parent: Parent var _latestElement1: E1! = nil var _latestElement2: E2! = nil var _latestElement3: E3! = nil init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 3, observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) subscription1.setDisposable(_parent._source1.subscribe(observer1)) subscription2.setDisposable(_parent._source2.subscribe(observer2)) subscription3.setDisposable(_parent._source3.subscribe(observer3)) return Disposables.create([ subscription1, subscription2, subscription3 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3) } } final class CombineLatest3 : Producer { typealias ResultSelector = (E1, E2, E3) throws -> R let _source1: Observable let _source2: Observable let _source3: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, resultSelector: @escaping ResultSelector) { _source1 = source1 _source2 = source2 _source3 = source3 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestSink3_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 4 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E) throws -> E) -> Observable { return CombineLatest4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4) -> Observable<(O1.E, O2.E, O3.E, O4.E)> { return CombineLatest4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), resultSelector: { ($0, $1, $2, $3) } ) } } final class CombineLatestSink4_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest4 let _parent: Parent var _latestElement1: E1! = nil var _latestElement2: E2! = nil var _latestElement3: E3! = nil var _latestElement4: E4! = nil init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 4, observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) subscription1.setDisposable(_parent._source1.subscribe(observer1)) subscription2.setDisposable(_parent._source2.subscribe(observer2)) subscription3.setDisposable(_parent._source3.subscribe(observer3)) subscription4.setDisposable(_parent._source4.subscribe(observer4)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4) } } final class CombineLatest4 : Producer { typealias ResultSelector = (E1, E2, E3, E4) throws -> R let _source1: Observable let _source2: Observable let _source3: Observable let _source4: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, resultSelector: @escaping ResultSelector) { _source1 = source1 _source2 = source2 _source3 = source3 _source4 = source4 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestSink4_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 5 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> E) -> Observable { return CombineLatest5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E)> { return CombineLatest5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), resultSelector: { ($0, $1, $2, $3, $4) } ) } } final class CombineLatestSink5_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest5 let _parent: Parent var _latestElement1: E1! = nil var _latestElement2: E2! = nil var _latestElement3: E3! = nil var _latestElement4: E4! = nil var _latestElement5: E5! = nil init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 5, observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) subscription1.setDisposable(_parent._source1.subscribe(observer1)) subscription2.setDisposable(_parent._source2.subscribe(observer2)) subscription3.setDisposable(_parent._source3.subscribe(observer3)) subscription4.setDisposable(_parent._source4.subscribe(observer4)) subscription5.setDisposable(_parent._source5.subscribe(observer5)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5) } } final class CombineLatest5 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> R let _source1: Observable let _source2: Observable let _source3: Observable let _source4: Observable let _source5: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, resultSelector: @escaping ResultSelector) { _source1 = source1 _source2 = source2 _source3 = source3 _source4 = source4 _source5 = source5 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestSink5_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 6 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> E) -> Observable { return CombineLatest6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E, O6.E)> { return CombineLatest6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), resultSelector: { ($0, $1, $2, $3, $4, $5) } ) } } final class CombineLatestSink6_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest6 let _parent: Parent var _latestElement1: E1! = nil var _latestElement2: E2! = nil var _latestElement3: E3! = nil var _latestElement4: E4! = nil var _latestElement5: E5! = nil var _latestElement6: E6! = nil init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 6, observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) subscription1.setDisposable(_parent._source1.subscribe(observer1)) subscription2.setDisposable(_parent._source2.subscribe(observer2)) subscription3.setDisposable(_parent._source3.subscribe(observer3)) subscription4.setDisposable(_parent._source4.subscribe(observer4)) subscription5.setDisposable(_parent._source5.subscribe(observer5)) subscription6.setDisposable(_parent._source6.subscribe(observer6)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5, subscription6 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6) } } final class CombineLatest6 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> R let _source1: Observable let _source2: Observable let _source3: Observable let _source4: Observable let _source5: Observable let _source6: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, resultSelector: @escaping ResultSelector) { _source1 = source1 _source2 = source2 _source3 = source3 _source4 = source4 _source5 = source5 _source6 = source6 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestSink6_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 7 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> E) -> Observable { return CombineLatest7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E)> { return CombineLatest7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), resultSelector: { ($0, $1, $2, $3, $4, $5, $6) } ) } } final class CombineLatestSink7_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest7 let _parent: Parent var _latestElement1: E1! = nil var _latestElement2: E2! = nil var _latestElement3: E3! = nil var _latestElement4: E4! = nil var _latestElement5: E5! = nil var _latestElement6: E6! = nil var _latestElement7: E7! = nil init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 7, observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) let observer7 = CombineLatestObserver(lock: _lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) subscription1.setDisposable(_parent._source1.subscribe(observer1)) subscription2.setDisposable(_parent._source2.subscribe(observer2)) subscription3.setDisposable(_parent._source3.subscribe(observer3)) subscription4.setDisposable(_parent._source4.subscribe(observer4)) subscription5.setDisposable(_parent._source5.subscribe(observer5)) subscription6.setDisposable(_parent._source6.subscribe(observer6)) subscription7.setDisposable(_parent._source7.subscribe(observer7)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5, subscription6, subscription7 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6, _latestElement7) } } final class CombineLatest7 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> R let _source1: Observable let _source2: Observable let _source3: Observable let _source4: Observable let _source5: Observable let _source6: Observable let _source7: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, resultSelector: @escaping ResultSelector) { _source1 = source1 _source2 = source2 _source3 = source3 _source4 = source4 _source5 = source5 _source6 = source6 _source7 = source7 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestSink7_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 8 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> E) -> Observable { return CombineLatest8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func combineLatest (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E)> { return CombineLatest8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), resultSelector: { ($0, $1, $2, $3, $4, $5, $6, $7) } ) } } final class CombineLatestSink8_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest8 let _parent: Parent var _latestElement1: E1! = nil var _latestElement2: E2! = nil var _latestElement3: E3! = nil var _latestElement4: E4! = nil var _latestElement5: E5! = nil var _latestElement6: E6! = nil var _latestElement7: E7! = nil var _latestElement8: E8! = nil init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 8, observer: observer, cancel: cancel) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() let subscription8 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) let observer7 = CombineLatestObserver(lock: _lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) let observer8 = CombineLatestObserver(lock: _lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self._latestElement8 = e }, this: subscription8) subscription1.setDisposable(_parent._source1.subscribe(observer1)) subscription2.setDisposable(_parent._source2.subscribe(observer2)) subscription3.setDisposable(_parent._source3.subscribe(observer3)) subscription4.setDisposable(_parent._source4.subscribe(observer4)) subscription5.setDisposable(_parent._source5.subscribe(observer5)) subscription6.setDisposable(_parent._source6.subscribe(observer6)) subscription7.setDisposable(_parent._source7.subscribe(observer7)) subscription8.setDisposable(_parent._source8.subscribe(observer8)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5, subscription6, subscription7, subscription8 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6, _latestElement7, _latestElement8) } } final class CombineLatest8 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> R let _source1: Observable let _source2: Observable let _source3: Observable let _source4: Observable let _source5: Observable let _source6: Observable let _source7: Observable let _source8: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, source8: Observable, resultSelector: @escaping ResultSelector) { _source1 = source1 _source2 = source2 _source3 = source3 _source4 = source4 _source5 = source5 _source6 = source6 _source7 = source7 _source8 = source8 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = CombineLatestSink8_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/CombineLatest.swift ================================================ // // CombineLatest.swift // RxSwift // // Created by Krunoslav Zaher on 3/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol CombineLatestProtocol : class { func next(_ index: Int) func fail(_ error: Swift.Error) func done(_ index: Int) } class CombineLatestSink : Sink , CombineLatestProtocol { typealias Element = O.E let _lock = RecursiveLock() private let _arity: Int private var _numberOfValues = 0 private var _numberOfDone = 0 private var _hasValue: [Bool] private var _isDone: [Bool] init(arity: Int, observer: O, cancel: Cancelable) { _arity = arity _hasValue = [Bool](repeating: false, count: arity) _isDone = [Bool](repeating: false, count: arity) super.init(observer: observer, cancel: cancel) } func getResult() throws -> Element { rxAbstractMethod() } func next(_ index: Int) { if !_hasValue[index] { _hasValue[index] = true _numberOfValues += 1 } if _numberOfValues == _arity { do { let result = try getResult() forwardOn(.next(result)) } catch let e { forwardOn(.error(e)) dispose() } } else { var allOthersDone = true for i in 0 ..< _arity { if i != index && !_isDone[i] { allOthersDone = false break } } if allOthersDone { forwardOn(.completed) dispose() } } } func fail(_ error: Swift.Error) { forwardOn(.error(error)) dispose() } func done(_ index: Int) { if _isDone[index] { return } _isDone[index] = true _numberOfDone += 1 if _numberOfDone == _arity { forwardOn(.completed) dispose() } } } final class CombineLatestObserver : ObserverType , LockOwnerType , SynchronizedOnType { typealias Element = ElementType typealias ValueSetter = (Element) -> Void private let _parent: CombineLatestProtocol let _lock: RecursiveLock private let _index: Int private let _this: Disposable private let _setLatestValue: ValueSetter init(lock: RecursiveLock, parent: CombineLatestProtocol, index: Int, setLatestValue: @escaping ValueSetter, this: Disposable) { _lock = lock _parent = parent _index = index _this = this _setLatestValue = setLatestValue } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next(let value): _setLatestValue(value) _parent.next(_index) case .error(let error): _this.dispose() _parent.fail(error) case .completed: _this.dispose() _parent.done(_index) } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Concat.swift ================================================ // // Concat.swift // RxSwift // // Created by Krunoslav Zaher on 3/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Concatenates the second observable sequence to `self` upon successful termination of `self`. - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html) - parameter second: Second observable sequence. - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence. */ public func concat(_ second: O) -> Observable where O.E == E { return Observable.concat([self.asObservable(), second.asObservable()]) } } extension ObservableType { /** Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully. This operator has tail recursive optimizations that will prevent stack overflow. Optimizations will be performed in cases equivalent to following: [1, [2, [3, .....].concat()].concat].concat() - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html) - returns: An observable sequence that contains the elements of each given sequence, in sequential order. */ public static func concat(_ sequence: S) -> Observable where S.Iterator.Element == Observable { return Concat(sources: sequence, count: nil) } /** Concatenates all observable sequences in the given collection, as long as the previous observable sequence terminated successfully. This operator has tail recursive optimizations that will prevent stack overflow. Optimizations will be performed in cases equivalent to following: [1, [2, [3, .....].concat()].concat].concat() - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html) - returns: An observable sequence that contains the elements of each given sequence, in sequential order. */ public static func concat(_ collection: S) -> Observable where S.Iterator.Element == Observable { return Concat(sources: collection, count: Int64(collection.count)) } /** Concatenates all observable sequences in the given collection, as long as the previous observable sequence terminated successfully. This operator has tail recursive optimizations that will prevent stack overflow. Optimizations will be performed in cases equivalent to following: [1, [2, [3, .....].concat()].concat].concat() - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html) - returns: An observable sequence that contains the elements of each given sequence, in sequential order. */ public static func concat(_ sources: Observable ...) -> Observable { return Concat(sources: sources, count: Int64(sources.count)) } } final fileprivate class ConcatSink : TailRecursiveSink , ObserverType where S.Iterator.Element : ObservableConvertibleType, S.Iterator.Element.E == O.E { typealias Element = O.E override init(observer: O, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } func on(_ event: Event){ switch event { case .next: forwardOn(event) case .error: forwardOn(event) dispose() case .completed: schedule(.moveNext) } } override func subscribeToNext(_ source: Observable) -> Disposable { return source.subscribe(self) } override func extract(_ observable: Observable) -> SequenceGenerator? { if let source = observable as? Concat { return (source._sources.makeIterator(), source._count) } else { return nil } } } final fileprivate class Concat : Producer where S.Iterator.Element : ObservableConvertibleType { typealias Element = S.Iterator.Element.E fileprivate let _sources: S fileprivate let _count: IntMax? init(sources: S, count: IntMax?) { _sources = sources _count = count } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = ConcatSink(observer: observer, cancel: cancel) let subscription = sink.run((_sources.makeIterator(), _count)) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Create.swift ================================================ // // Create.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { // MARK: create /** Creates an observable sequence from a specified subscribe method implementation. - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html) - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method. - returns: The observable sequence with the specified implementation for the `subscribe` method. */ public static func create(_ subscribe: @escaping (AnyObserver) -> Disposable) -> Observable { return AnonymousObservable(subscribe) } } final fileprivate class AnonymousObservableSink : Sink, ObserverType { typealias E = O.E typealias Parent = AnonymousObservable // state private var _isStopped: AtomicInt = 0 #if DEBUG fileprivate let _synchronizationTracker = SynchronizationTracker() #endif override init(observer: O, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { #if DEBUG _synchronizationTracker.register(synchronizationErrorMessage: .default) defer { _synchronizationTracker.unregister() } #endif switch event { case .next: if _isStopped == 1 { return } forwardOn(event) case .error, .completed: if AtomicCompareAndSwap(0, 1, &_isStopped) { forwardOn(event) dispose() } } } func run(_ parent: Parent) -> Disposable { return parent._subscribeHandler(AnyObserver(self)) } } final fileprivate class AnonymousObservable : Producer { typealias SubscribeHandler = (AnyObserver) -> Disposable let _subscribeHandler: SubscribeHandler init(_ subscribeHandler: @escaping SubscribeHandler) { _subscribeHandler = subscribeHandler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = AnonymousObservableSink(observer: observer, cancel: cancel) let subscription = sink.run(self) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Debounce.swift ================================================ // // Debounce.swift // RxSwift // // Created by Krunoslav Zaher on 9/11/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers. - seealso: [debounce operator on reactivex.io](http://reactivex.io/documentation/operators/debounce.html) - parameter dueTime: Throttling duration for each element. - parameter scheduler: Scheduler to run the throttle timers on. - returns: The throttled sequence. */ public func debounce(_ dueTime: RxTimeInterval, scheduler: SchedulerType) -> Observable { return Debounce(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler) } } final fileprivate class DebounceSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType { typealias Element = O.E typealias ParentType = Debounce private let _parent: ParentType let _lock = RecursiveLock() // state private var _id = 0 as UInt64 private var _value: Element? = nil let cancellable = SerialDisposable() init(parent: ParentType, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let subscription = _parent._source.subscribe(self) return Disposables.create(subscription, cancellable) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next(let element): _id = _id &+ 1 let currentId = _id _value = element let scheduler = _parent._scheduler let dueTime = _parent._dueTime let d = SingleAssignmentDisposable() self.cancellable.disposable = d d.setDisposable(scheduler.scheduleRelative(currentId, dueTime: dueTime, action: self.propagate)) case .error: _value = nil forwardOn(event) dispose() case .completed: if let value = _value { _value = nil forwardOn(.next(value)) } forwardOn(.completed) dispose() } } func propagate(_ currentId: UInt64) -> Disposable { _lock.lock(); defer { _lock.unlock() } // { let originalValue = _value if let value = originalValue, _id == currentId { _value = nil forwardOn(.next(value)) } // } return Disposables.create() } } final fileprivate class Debounce : Producer { fileprivate let _source: Observable fileprivate let _dueTime: RxTimeInterval fileprivate let _scheduler: SchedulerType init(source: Observable, dueTime: RxTimeInterval, scheduler: SchedulerType) { _source = source _dueTime = dueTime _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = DebounceSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Debug.swift ================================================ // // Debug.swift // RxSwift // // Created by Krunoslav Zaher on 5/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.Date import class Foundation.DateFormatter extension ObservableType { /** Prints received events for all observers on standard output. - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html) - parameter identifier: Identifier that is printed together with event description to standard output. - parameter trimOutput: Should output be trimmed to max 40 characters. - returns: An observable sequence whose events are printed to standard output. */ public func debug(_ identifier: String? = nil, trimOutput: Bool = false, file: String = #file, line: UInt = #line, function: String = #function) -> Observable { return Debug(source: self, identifier: identifier, trimOutput: trimOutput, file: file, line: line, function: function) } } fileprivate let dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" fileprivate func logEvent(_ identifier: String, dateFormat: DateFormatter, content: String) { print("\(dateFormat.string(from: Date())): \(identifier) -> \(content)") } final fileprivate class DebugSink : Sink, ObserverType where O.E == Source.E { typealias Element = O.E typealias Parent = Debug private let _parent: Parent private let _timestampFormatter = DateFormatter() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _timestampFormatter.dateFormat = dateFormat logEvent(_parent._identifier, dateFormat: _timestampFormatter, content: "subscribed") super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { let maxEventTextLength = 40 let eventText = "\(event)" let eventNormalized = (eventText.count > maxEventTextLength) && _parent._trimOutput ? String(eventText.prefix(maxEventTextLength / 2)) + "..." + String(eventText.suffix(maxEventTextLength / 2)) : eventText logEvent(_parent._identifier, dateFormat: _timestampFormatter, content: "Event \(eventNormalized)") forwardOn(event) if event.isStopEvent { dispose() } } override func dispose() { if !self.disposed { logEvent(_parent._identifier, dateFormat: _timestampFormatter, content: "isDisposed") } super.dispose() } } final fileprivate class Debug : Producer { fileprivate let _identifier: String fileprivate let _trimOutput: Bool fileprivate let _source: Source init(source: Source, identifier: String?, trimOutput: Bool, file: String, line: UInt, function: String) { _trimOutput = trimOutput if let identifier = identifier { _identifier = identifier } else { let trimmedFile: String if let lastIndex = file.lastIndexOf("/") { trimmedFile = String(file[file.index(after: lastIndex) ..< file.endIndex]) } else { trimmedFile = file } _identifier = "\(trimmedFile):\(line) (\(function))" } _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Source.E { let sink = DebugSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/DefaultIfEmpty.swift ================================================ // // DefaultIfEmpty.swift // RxSwift // // Created by sergdort on 23/12/2016. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Emits elements from the source observable sequence, or a default element if the source observable sequence is empty. - seealso: [DefaultIfEmpty operator on reactivex.io](http://reactivex.io/documentation/operators/defaultifempty.html) - parameter default: Default element to be sent if the source does not emit any elements - returns: An observable sequence which emits default element end completes in case the original sequence is empty */ public func ifEmpty(default: E) -> Observable { return DefaultIfEmpty(source: self.asObservable(), default: `default`) } } final fileprivate class DefaultIfEmptySink: Sink, ObserverType { typealias E = O.E private let _default: E private var _isEmpty = true init(default: E, observer: O, cancel: Cancelable) { _default = `default` super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(_): _isEmpty = false forwardOn(event) case .error(_): forwardOn(event) dispose() case .completed: if _isEmpty { forwardOn(.next(_default)) } forwardOn(.completed) dispose() } } } final fileprivate class DefaultIfEmpty: Producer { private let _source: Observable private let _default: SourceType init(source: Observable, `default`: SourceType) { _source = source _default = `default` } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == SourceType { let sink = DefaultIfEmptySink(default: _default, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Deferred.swift ================================================ // // Deferred.swift // RxSwift // // Created by Krunoslav Zaher on 4/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. - seealso: [defer operator on reactivex.io](http://reactivex.io/documentation/operators/defer.html) - parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence. - returns: An observable sequence whose observers trigger an invocation of the given observable factory function. */ public static func deferred(_ observableFactory: @escaping () throws -> Observable) -> Observable { return Deferred(observableFactory: observableFactory) } } final fileprivate class DeferredSink : Sink, ObserverType where S.E == O.E { typealias E = O.E private let _observableFactory: () throws -> S init(observableFactory: @escaping () throws -> S, observer: O, cancel: Cancelable) { _observableFactory = observableFactory super.init(observer: observer, cancel: cancel) } func run() -> Disposable { do { let result = try _observableFactory() return result.subscribe(self) } catch let e { forwardOn(.error(e)) dispose() return Disposables.create() } } func on(_ event: Event) { forwardOn(event) switch event { case .next: break case .error: dispose() case .completed: dispose() } } } final fileprivate class Deferred : Producer { typealias Factory = () throws -> S private let _observableFactory : Factory init(observableFactory: @escaping Factory) { _observableFactory = observableFactory } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == S.E { let sink = DeferredSink(observableFactory: _observableFactory, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Delay.swift ================================================ // // Delay.swift // RxSwift // // Created by tarunon on 2016/02/09. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import struct Foundation.Date extension ObservableType { /** Returns an observable sequence by the source observable sequence shifted forward in time by a specified delay. Error events from the source observable sequence are not delayed. - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html) - parameter dueTime: Relative time shift of the source by. - parameter scheduler: Scheduler to run the subscription delay timer on. - returns: the source Observable shifted in time by the specified delay. */ public func delay(_ dueTime: RxTimeInterval, scheduler: SchedulerType) -> Observable { return Delay(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler) } } final fileprivate class DelaySink : Sink , ObserverType { typealias E = O.E typealias Source = Observable typealias DisposeKey = Bag.KeyType private let _lock = RecursiveLock() private let _dueTime: RxTimeInterval private let _scheduler: SchedulerType private let _sourceSubscription = SingleAssignmentDisposable() private let _cancelable = SerialDisposable() // is scheduled some action private var _active = false // is "run loop" on different scheduler running private var _running = false private var _errorEvent: Event? = nil // state private var _queue = Queue<(eventTime: RxTime, event: Event)>(capacity: 0) private var _disposed = false init(observer: O, dueTime: RxTimeInterval, scheduler: SchedulerType, cancel: Cancelable) { _dueTime = dueTime _scheduler = scheduler super.init(observer: observer, cancel: cancel) } // All of these complications in this method are caused by the fact that // error should be propagated immediately. Error can be potentially received on different // scheduler so this process needs to be synchronized somehow. // // Another complication is that scheduler is potentially concurrent so internal queue is used. func drainQueue(state: (), scheduler: AnyRecursiveScheduler<()>) { _lock.lock() // { let hasFailed = _errorEvent != nil if !hasFailed { _running = true } _lock.unlock() // } if hasFailed { return } var ranAtLeastOnce = false while true { _lock.lock() // { let errorEvent = _errorEvent let eventToForwardImmediatelly = ranAtLeastOnce ? nil : _queue.dequeue()?.event let nextEventToScheduleOriginalTime: Date? = ranAtLeastOnce && !_queue.isEmpty ? _queue.peek().eventTime : nil if let _ = errorEvent { } else { if let _ = eventToForwardImmediatelly { } else if let _ = nextEventToScheduleOriginalTime { _running = false } else { _running = false _active = false } } _lock.unlock() // { if let errorEvent = errorEvent { self.forwardOn(errorEvent) self.dispose() return } else { if let eventToForwardImmediatelly = eventToForwardImmediatelly { ranAtLeastOnce = true self.forwardOn(eventToForwardImmediatelly) if case .completed = eventToForwardImmediatelly { self.dispose() return } } else if let nextEventToScheduleOriginalTime = nextEventToScheduleOriginalTime { let elapsedTime = _scheduler.now.timeIntervalSince(nextEventToScheduleOriginalTime) let interval = _dueTime - elapsedTime let normalizedInterval = interval < 0.0 ? 0.0 : interval scheduler.schedule((), dueTime: normalizedInterval) return } else { return } } } } func on(_ event: Event) { if event.isStopEvent { _sourceSubscription.dispose() } switch event { case .error(_): _lock.lock() // { let shouldSendImmediatelly = !_running _queue = Queue(capacity: 0) _errorEvent = event _lock.unlock() // } if shouldSendImmediatelly { forwardOn(event) dispose() } default: _lock.lock() // { let shouldSchedule = !_active _active = true _queue.enqueue((_scheduler.now, event)) _lock.unlock() // } if shouldSchedule { _cancelable.disposable = _scheduler.scheduleRecursive((), dueTime: _dueTime, action: self.drainQueue) } } } func run(source: Observable) -> Disposable { _sourceSubscription.setDisposable(source.subscribe(self)) return Disposables.create(_sourceSubscription, _cancelable) } } final fileprivate class Delay: Producer { private let _source: Observable private let _dueTime: RxTimeInterval private let _scheduler: SchedulerType init(source: Observable, dueTime: RxTimeInterval, scheduler: SchedulerType) { _source = source _dueTime = dueTime _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = DelaySink(observer: observer, dueTime: _dueTime, scheduler: _scheduler, cancel: cancel) let subscription = sink.run(source: _source) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/DelaySubscription.swift ================================================ // // DelaySubscription.swift // RxSwift // // Created by Krunoslav Zaher on 6/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Time shifts the observable sequence by delaying the subscription with the specified relative time duration, using the specified scheduler to run timers. - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html) - parameter dueTime: Relative time shift of the subscription. - parameter scheduler: Scheduler to run the subscription delay timer on. - returns: Time-shifted sequence. */ public func delaySubscription(_ dueTime: RxTimeInterval, scheduler: SchedulerType) -> Observable { return DelaySubscription(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler) } } final fileprivate class DelaySubscriptionSink : Sink, ObserverType { typealias E = O.E typealias Parent = DelaySubscription private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { forwardOn(event) if event.isStopEvent { dispose() } } } final fileprivate class DelaySubscription: Producer { private let _source: Observable private let _dueTime: RxTimeInterval private let _scheduler: SchedulerType init(source: Observable, dueTime: RxTimeInterval, scheduler: SchedulerType) { _source = source _dueTime = dueTime _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = DelaySubscriptionSink(parent: self, observer: observer, cancel: cancel) let subscription = _scheduler.scheduleRelative((), dueTime: _dueTime) { _ in return self._source.subscribe(sink) } return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Dematerialize.swift ================================================ // // Dematerialize.swift // RxSwift // // Created by Jamie Pinkham on 3/13/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // extension ObservableType where E: EventConvertible { /** Convert any previously materialized Observable into it's original form. - seealso: [materialize operator on reactivex.io](http://reactivex.io/documentation/operators/materialize-dematerialize.html) - returns: The dematerialized observable sequence. */ public func dematerialize() -> Observable { return Dematerialize(source: self.asObservable()) } } fileprivate final class DematerializeSink: Sink, ObserverType where O.E == Element.ElementType { fileprivate func on(_ event: Event) { switch event { case .next(let element): forwardOn(element.event) if element.event.isStopEvent { dispose() } case .completed: forwardOn(.completed) dispose() case .error(let error): forwardOn(.error(error)) dispose() } } } final fileprivate class Dematerialize: Producer { private let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element.ElementType { let sink = DematerializeSink(observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/DistinctUntilChanged.swift ================================================ // // DistinctUntilChanged.swift // RxSwift // // Created by Krunoslav Zaher on 3/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType where E: Equatable { /** Returns an observable sequence that contains only distinct contiguous elements according to equality operator. - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html) - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence. */ public func distinctUntilChanged() -> Observable { return self.distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) } } extension ObservableType { /** Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`. - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html) - parameter keySelector: A function to compute the comparison key for each element. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. */ public func distinctUntilChanged(_ keySelector: @escaping (E) throws -> K) -> Observable { return self.distinctUntilChanged(keySelector, comparer: { $0 == $1 }) } /** Returns an observable sequence that contains only distinct contiguous elements according to the `comparer`. - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html) - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. */ public func distinctUntilChanged(_ comparer: @escaping (E, E) throws -> Bool) -> Observable { return self.distinctUntilChanged({ $0 }, comparer: comparer) } /** Returns an observable sequence that contains only distinct contiguous elements according to the keySelector and the comparer. - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html) - parameter keySelector: A function to compute the comparison key for each element. - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. */ public func distinctUntilChanged(_ keySelector: @escaping (E) throws -> K, comparer: @escaping (K, K) throws -> Bool) -> Observable { return DistinctUntilChanged(source: self.asObservable(), selector: keySelector, comparer: comparer) } } final fileprivate class DistinctUntilChangedSink: Sink, ObserverType { typealias E = O.E private let _parent: DistinctUntilChanged private var _currentKey: Key? = nil init(parent: DistinctUntilChanged, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): do { let key = try _parent._selector(value) var areEqual = false if let currentKey = _currentKey { areEqual = try _parent._comparer(currentKey, key) } if areEqual { return } _currentKey = key forwardOn(event) } catch let error { forwardOn(.error(error)) dispose() } case .error, .completed: forwardOn(event) dispose() } } } final fileprivate class DistinctUntilChanged: Producer { typealias KeySelector = (Element) throws -> Key typealias EqualityComparer = (Key, Key) throws -> Bool fileprivate let _source: Observable fileprivate let _selector: KeySelector fileprivate let _comparer: EqualityComparer init(source: Observable, selector: @escaping KeySelector, comparer: @escaping EqualityComparer) { _source = source _selector = selector _comparer = comparer } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = DistinctUntilChangedSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Do.swift ================================================ // // Do.swift // RxSwift // // Created by Krunoslav Zaher on 2/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html) - parameter onNext: Action to invoke for each element in the observable sequence. - parameter onError: Action to invoke upon errored termination of the observable sequence. - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - parameter onSubscribe: Action to invoke before subscribing to source observable sequence. - parameter onSubscribed: Action to invoke after subscribing to source observable sequence. - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed. - returns: The source sequence with the side-effecting behavior applied. */ public func `do`(onNext: ((E) throws -> Void)? = nil, onError: ((Swift.Error) throws -> Void)? = nil, onCompleted: (() throws -> Void)? = nil, onSubscribe: (() -> ())? = nil, onSubscribed: (() -> ())? = nil, onDispose: (() -> ())? = nil) -> Observable { return Do(source: self.asObservable(), eventHandler: { e in switch e { case .next(let element): try onNext?(element) case .error(let e): try onError?(e) case .completed: try onCompleted?() } }, onSubscribe: onSubscribe, onSubscribed: onSubscribed, onDispose: onDispose) } } final fileprivate class DoSink : Sink, ObserverType { typealias Element = O.E typealias EventHandler = (Event) throws -> Void private let _eventHandler: EventHandler init(eventHandler: @escaping EventHandler, observer: O, cancel: Cancelable) { _eventHandler = eventHandler super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { do { try _eventHandler(event) forwardOn(event) if event.isStopEvent { dispose() } } catch let error { forwardOn(.error(error)) dispose() } } } final fileprivate class Do : Producer { typealias EventHandler = (Event) throws -> Void fileprivate let _source: Observable fileprivate let _eventHandler: EventHandler fileprivate let _onSubscribe: (() -> ())? fileprivate let _onSubscribed: (() -> ())? fileprivate let _onDispose: (() -> ())? init(source: Observable, eventHandler: @escaping EventHandler, onSubscribe: (() -> ())?, onSubscribed: (() -> ())?, onDispose: (() -> ())?) { _source = source _eventHandler = eventHandler _onSubscribe = onSubscribe _onSubscribed = onSubscribed _onDispose = onDispose } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { _onSubscribe?() let sink = DoSink(eventHandler: _eventHandler, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) _onSubscribed?() let onDispose = _onDispose let allSubscriptions = Disposables.create { subscription.dispose() onDispose?() } return (sink: sink, subscription: allSubscriptions) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/ElementAt.swift ================================================ // // ElementAt.swift // RxSwift // // Created by Junior B. on 21/10/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns a sequence emitting only element _n_ emitted by an Observable - seealso: [elementAt operator on reactivex.io](http://reactivex.io/documentation/operators/elementat.html) - parameter index: The index of the required element (starting from 0). - returns: An observable sequence that emits the desired element as its own sole emission. */ public func elementAt(_ index: Int) -> Observable { return ElementAt(source: asObservable(), index: index, throwOnEmpty: true) } } final fileprivate class ElementAtSink : Sink, ObserverType { typealias SourceType = O.E typealias Parent = ElementAt let _parent: Parent var _i: Int init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _i = parent._index super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(_): if (_i == 0) { forwardOn(event) forwardOn(.completed) self.dispose() } do { let _ = try decrementChecked(&_i) } catch(let e) { forwardOn(.error(e)) dispose() return } case .error(let e): forwardOn(.error(e)) self.dispose() case .completed: if (_parent._throwOnEmpty) { forwardOn(.error(RxError.argumentOutOfRange)) } else { forwardOn(.completed) } self.dispose() } } } final fileprivate class ElementAt : Producer { let _source: Observable let _throwOnEmpty: Bool let _index: Int init(source: Observable, index: Int, throwOnEmpty: Bool) { if index < 0 { rxFatalError("index can't be negative") } self._source = source self._index = index self._throwOnEmpty = throwOnEmpty } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == SourceType { let sink = ElementAtSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Empty.swift ================================================ // // Empty.swift // RxSwift // // Created by Krunoslav Zaher on 8/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message. - seealso: [empty operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html) - returns: An observable sequence with no elements. */ public static func empty() -> Observable { return EmptyProducer() } } final fileprivate class EmptyProducer : Producer { override func subscribe(_ observer: O) -> Disposable where O.E == Element { observer.on(.completed) return Disposables.create() } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Enumerated.swift ================================================ // // Enumerated.swift // RxSwift // // Created by Krunoslav Zaher on 8/6/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Enumerates the elements of an observable sequence. - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html) - returns: An observable sequence that contains tuples of source sequence elements and their indexes. */ public func enumerated() -> Observable<(index: Int, element: E)> { return Enumerated(source: self.asObservable()) } } final fileprivate class EnumeratedSink: Sink, ObserverType where O.E == (index: Int, element: Element) { typealias E = Element var index = 0 func on(_ event: Event) { switch event { case .next(let value): do { let nextIndex = try incrementChecked(&index) let next = (index: nextIndex, element: value) forwardOn(.next(next)) } catch let e { forwardOn(.error(e)) dispose() } case .completed: forwardOn(.completed) dispose() case .error(let error): forwardOn(.error(error)) dispose() } } } final fileprivate class Enumerated : Producer<(index: Int, element: Element)> { private let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == (index: Int, element: Element) { let sink = EnumeratedSink(observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Error.swift ================================================ // // Error.swift // RxSwift // // Created by Krunoslav Zaher on 8/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns an observable sequence that terminates with an `error`. - seealso: [throw operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html) - returns: The observable sequence that terminates with specified error. */ public static func error(_ error: Swift.Error) -> Observable { return ErrorProducer(error: error) } } final fileprivate class ErrorProducer : Producer { private let _error: Swift.Error init(error: Swift.Error) { _error = error } override func subscribe(_ observer: O) -> Disposable where O.E == Element { observer.on(.error(_error)) return Disposables.create() } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Filter.swift ================================================ // // Filter.swift // RxSwift // // Created by Krunoslav Zaher on 2/17/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Filters the elements of an observable sequence based on a predicate. - seealso: [filter operator on reactivex.io](http://reactivex.io/documentation/operators/filter.html) - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. */ public func filter(_ predicate: @escaping (E) throws -> Bool) -> Observable { return Filter(source: asObservable(), predicate: predicate) } } extension ObservableType { /** Skips elements and completes (or errors) when the receiver completes (or errors). Equivalent to filter that always returns false. - seealso: [ignoreElements operator on reactivex.io](http://reactivex.io/documentation/operators/ignoreelements.html) - returns: An observable sequence that skips all elements of the source sequence. */ public func ignoreElements() -> Completable { return flatMap { _ in return Observable.empty() } .asCompletable() } } final fileprivate class FilterSink: Sink, ObserverType { typealias Predicate = (Element) throws -> Bool typealias Element = O.E private let _predicate: Predicate init(predicate: @escaping Predicate, observer: O, cancel: Cancelable) { _predicate = predicate super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): do { let satisfies = try _predicate(value) if satisfies { forwardOn(.next(value)) } } catch let e { forwardOn(.error(e)) dispose() } case .completed, .error: forwardOn(event) dispose() } } } final fileprivate class Filter : Producer { typealias Predicate = (Element) throws -> Bool private let _source: Observable private let _predicate: Predicate init(source: Observable, predicate: @escaping Predicate) { _source = source _predicate = predicate } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = FilterSink(predicate: _predicate, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/First.swift ================================================ // // First.swift // RxSwift // // Created by Krunoslav Zaher on 7/31/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // fileprivate final class FirstSink : Sink, ObserverType where O.E == Element? { typealias E = Element typealias Parent = First func on(_ event: Event) { switch event { case .next(let value): forwardOn(.next(value)) forwardOn(.completed) dispose() case .error(let error): forwardOn(.error(error)) dispose() case .completed: forwardOn(.next(nil)) forwardOn(.completed) dispose() } } } final class First: Producer { fileprivate let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element? { let sink = FirstSink(observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Generate.swift ================================================ // // Generate.swift // RxSwift // // Created by Krunoslav Zaher on 9/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Generates an observable sequence by running a state-driven loop producing the sequence's elements, using the specified scheduler to run the loop send out observer messages. - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html) - parameter initialState: Initial state. - parameter condition: Condition to terminate generation (upon returning `false`). - parameter iterate: Iteration step function. - parameter scheduler: Scheduler on which to run the generator loop. - returns: The generated sequence. */ public static func generate(initialState: E, condition: @escaping (E) throws -> Bool, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance, iterate: @escaping (E) throws -> E) -> Observable { return Generate(initialState: initialState, condition: condition, iterate: iterate, resultSelector: { $0 }, scheduler: scheduler) } } final fileprivate class GenerateSink : Sink { typealias Parent = Generate private let _parent: Parent private var _state: S init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _state = parent._initialState super.init(observer: observer, cancel: cancel) } func run() -> Disposable { return _parent._scheduler.scheduleRecursive(true) { (isFirst, recurse) -> Void in do { if !isFirst { self._state = try self._parent._iterate(self._state) } if try self._parent._condition(self._state) { let result = try self._parent._resultSelector(self._state) self.forwardOn(.next(result)) recurse(false) } else { self.forwardOn(.completed) self.dispose() } } catch let error { self.forwardOn(.error(error)) self.dispose() } } } } final fileprivate class Generate : Producer { fileprivate let _initialState: S fileprivate let _condition: (S) throws -> Bool fileprivate let _iterate: (S) throws -> S fileprivate let _resultSelector: (S) throws -> E fileprivate let _scheduler: ImmediateSchedulerType init(initialState: S, condition: @escaping (S) throws -> Bool, iterate: @escaping (S) throws -> S, resultSelector: @escaping (S) throws -> E, scheduler: ImmediateSchedulerType) { _initialState = initialState _condition = condition _iterate = iterate _resultSelector = resultSelector _scheduler = scheduler super.init() } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = GenerateSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/GroupBy.swift ================================================ // // GroupBy.swift // RxSwift // // Created by Tomi Koskinen on 01/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /* Groups the elements of an observable sequence according to a specified key selector function. - seealso: [groupBy operator on reactivex.io](http://reactivex.io/documentation/operators/groupby.html) - parameter keySelector: A function to extract the key for each element. - returns: A sequence of observable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. */ public func groupBy(keySelector: @escaping (E) throws -> K) -> Observable> { return GroupBy(source: self.asObservable(), selector: keySelector) } } final fileprivate class GroupedObservableImpl : Observable { private var _subject: PublishSubject private var _refCount: RefCountDisposable init(key: Key, subject: PublishSubject, refCount: RefCountDisposable) { _subject = subject _refCount = refCount } override public func subscribe(_ observer: O) -> Disposable where O.E == E { let release = _refCount.retain() let subscription = _subject.subscribe(observer) return Disposables.create(release, subscription) } } final fileprivate class GroupBySink : Sink , ObserverType where O.E == GroupedObservable { typealias E = Element typealias ResultType = O.E typealias Parent = GroupBy private let _parent: Parent private let _subscription = SingleAssignmentDisposable() private var _refCountDisposable: RefCountDisposable! private var _groupedSubjectTable: [Key: PublishSubject] init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _groupedSubjectTable = [Key: PublishSubject]() super.init(observer: observer, cancel: cancel) } func run() -> Disposable { _refCountDisposable = RefCountDisposable(disposable: _subscription) _subscription.setDisposable(_parent._source.subscribe(self)) return _refCountDisposable } private func onGroupEvent(key: Key, value: Element) { if let writer = _groupedSubjectTable[key] { writer.on(.next(value)) } else { let writer = PublishSubject() _groupedSubjectTable[key] = writer let group = GroupedObservable( key: key, source: GroupedObservableImpl(key: key, subject: writer, refCount: _refCountDisposable) ) forwardOn(.next(group)) writer.on(.next(value)) } } final func on(_ event: Event) { switch event { case let .next(value): do { let groupKey = try _parent._selector(value) onGroupEvent(key: groupKey, value: value) } catch let e { error(e) return } case let .error(e): error(e) case .completed: forwardOnGroups(event: .completed) forwardOn(.completed) _subscription.dispose() dispose() } } final func error(_ error: Swift.Error) { forwardOnGroups(event: .error(error)) forwardOn(.error(error)) _subscription.dispose() dispose() } final func forwardOnGroups(event: Event) { for writer in _groupedSubjectTable.values { writer.on(event) } } } final fileprivate class GroupBy: Producer> { typealias KeySelector = (Element) throws -> Key fileprivate let _source: Observable fileprivate let _selector: KeySelector init(source: Observable, selector: @escaping KeySelector) { _source = source _selector = selector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == GroupedObservable { let sink = GroupBySink(parent: self, observer: observer, cancel: cancel) return (sink: sink, subscription: sink.run()) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Just.swift ================================================ // // Just.swift // RxSwift // // Created by Krunoslav Zaher on 8/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns an observable sequence that contains a single element. - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html) - parameter element: Single element in the resulting observable sequence. - returns: An observable sequence containing the single specified element. */ public static func just(_ element: E) -> Observable { return Just(element: element) } /** Returns an observable sequence that contains a single element. - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html) - parameter element: Single element in the resulting observable sequence. - parameter: Scheduler to send the single element on. - returns: An observable sequence containing the single specified element. */ public static func just(_ element: E, scheduler: ImmediateSchedulerType) -> Observable { return JustScheduled(element: element, scheduler: scheduler) } } final fileprivate class JustScheduledSink : Sink { typealias Parent = JustScheduled private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let scheduler = _parent._scheduler return scheduler.schedule(_parent._element) { element in self.forwardOn(.next(element)) return scheduler.schedule(()) { _ in self.forwardOn(.completed) self.dispose() return Disposables.create() } } } } final fileprivate class JustScheduled : Producer { fileprivate let _scheduler: ImmediateSchedulerType fileprivate let _element: Element init(element: Element, scheduler: ImmediateSchedulerType) { _scheduler = scheduler _element = element } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = JustScheduledSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } final fileprivate class Just : Producer { private let _element: Element init(element: Element) { _element = element } override func subscribe(_ observer: O) -> Disposable where O.E == Element { observer.on(.next(_element)) observer.on(.completed) return Disposables.create() } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Map.swift ================================================ // // Map.swift // RxSwift // // Created by Krunoslav Zaher on 3/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Projects each element of an observable sequence into a new form. - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html) - parameter transform: A transform function to apply to each source element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ public func map(_ transform: @escaping (E) throws -> R) -> Observable { return self.asObservable().composeMap(transform) } } final fileprivate class MapSink : Sink, ObserverType { typealias Transform = (SourceType) throws -> ResultType typealias ResultType = O.E typealias Element = SourceType private let _transform: Transform init(transform: @escaping Transform, observer: O, cancel: Cancelable) { _transform = transform super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let element): do { let mappedElement = try _transform(element) forwardOn(.next(mappedElement)) } catch let e { forwardOn(.error(e)) dispose() } case .error(let error): forwardOn(.error(error)) dispose() case .completed: forwardOn(.completed) dispose() } } } #if TRACE_RESOURCES fileprivate var _numberOfMapOperators: AtomicInt = 0 extension Resources { public static var numberOfMapOperators: Int32 { return _numberOfMapOperators.valueSnapshot() } } #endif internal func _map(source: Observable, transform: @escaping (Element) throws -> R) -> Observable { return Map(source: source, transform: transform) } final fileprivate class Map: Producer { typealias Transform = (SourceType) throws -> ResultType private let _source: Observable private let _transform: Transform init(source: Observable, transform: @escaping Transform) { _source = source _transform = transform #if TRACE_RESOURCES let _ = AtomicIncrement(&_numberOfMapOperators) #endif } override func composeMap(_ selector: @escaping (ResultType) throws -> R) -> Observable { let originalSelector = _transform return Map(source: _source, transform: { (s: SourceType) throws -> R in let r: ResultType = try originalSelector(s) return try selector(r) }) } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == ResultType { let sink = MapSink(transform: _transform, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } #if TRACE_RESOURCES deinit { let _ = AtomicDecrement(&_numberOfMapOperators) } #endif } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Materialize.swift ================================================ // // Materialize.swift // RxSwift // // Created by sergdort on 08/03/2017. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Convert any Observable into an Observable of its events. - seealso: [materialize operator on reactivex.io](http://reactivex.io/documentation/operators/materialize-dematerialize.html) - returns: An observable sequence that wraps events in an Event. The returned Observable never errors, but it does complete after observing all of the events of the underlying Observable. */ public func materialize() -> Observable> { return Materialize(source: self.asObservable()) } } fileprivate final class MaterializeSink: Sink, ObserverType where O.E == Event { func on(_ event: Event) { forwardOn(.next(event)) if event.isStopEvent { forwardOn(.completed) dispose() } } } final fileprivate class Materialize: Producer> { private let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = MaterializeSink(observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Merge.swift ================================================ // // Merge.swift // RxSwift // // Created by Krunoslav Zaher on 3/28/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html) - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ public func flatMap(_ selector: @escaping (E) throws -> O) -> Observable { return FlatMap(source: asObservable(), selector: selector) } } extension ObservableType { /** Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. If element is received while there is some projected observable sequence being merged it will simply be ignored. - seealso: [flatMapFirst operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html) - parameter selector: A transform function to apply to element that was observed while no observable is executing in parallel. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence that was received while no other sequence was being calculated. */ public func flatMapFirst(_ selector: @escaping (E) throws -> O) -> Observable { return FlatMapFirst(source: asObservable(), selector: selector) } } extension ObservableType where E : ObservableConvertibleType { /** Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - returns: The observable sequence that merges the elements of the observable sequences. */ public func merge() -> Observable { return Merge(source: asObservable()) } /** Merges elements from all inner observable sequences into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently. - returns: The observable sequence that merges the elements of the inner sequences. */ public func merge(maxConcurrent: Int) -> Observable { return MergeLimited(source: asObservable(), maxConcurrent: maxConcurrent) } } extension ObservableType where E : ObservableConvertibleType { /** Concatenates all inner observable sequences, as long as the previous observable sequence terminated successfully. - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html) - returns: An observable sequence that contains the elements of each observed inner sequence, in sequential order. */ public func concat() -> Observable { return merge(maxConcurrent: 1) } } extension ObservableType { /** Merges elements from all observable sequences from collection into a single observable sequence. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - parameter sources: Collection of observable sequences to merge. - returns: The observable sequence that merges the elements of the observable sequences. */ public static func merge(_ sources: C) -> Observable where C.Iterator.Element == Observable { return MergeArray(sources: Array(sources)) } /** Merges elements from all observable sequences from array into a single observable sequence. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - parameter sources: Array of observable sequences to merge. - returns: The observable sequence that merges the elements of the observable sequences. */ public static func merge(_ sources: [Observable]) -> Observable { return MergeArray(sources: sources) } /** Merges elements from all observable sequences into a single observable sequence. - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html) - parameter sources: Collection of observable sequences to merge. - returns: The observable sequence that merges the elements of the observable sequences. */ public static func merge(_ sources: Observable...) -> Observable { return MergeArray(sources: sources) } } // MARK: concatMap extension ObservableType { /** Projects each element of an observable sequence to an observable sequence and concatenates the resulting observable sequences into one observable sequence. - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html) - returns: An observable sequence that contains the elements of each observed inner sequence, in sequential order. */ public func concatMap(_ selector: @escaping (E) throws -> O) -> Observable { return ConcatMap(source: asObservable(), selector: selector) } } fileprivate final class MergeLimitedSinkIter : ObserverType , LockOwnerType , SynchronizedOnType where SourceSequence.E == Observer.E { typealias E = Observer.E typealias DisposeKey = CompositeDisposable.DisposeKey typealias Parent = MergeLimitedSink private let _parent: Parent private let _disposeKey: DisposeKey var _lock: RecursiveLock { return _parent._lock } init(parent: Parent, disposeKey: DisposeKey) { _parent = parent _disposeKey = disposeKey } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next: _parent.forwardOn(event) case .error: _parent.forwardOn(event) _parent.dispose() case .completed: _parent._group.remove(for: _disposeKey) if let next = _parent._queue.dequeue() { _parent.subscribe(next, group: _parent._group) } else { _parent._activeCount = _parent._activeCount - 1 if _parent._stopped && _parent._activeCount == 0 { _parent.forwardOn(.completed) _parent.dispose() } } } } } fileprivate final class ConcatMapSink: MergeLimitedSink where Observer.E == SourceSequence.E { typealias Selector = (SourceElement) throws -> SourceSequence private let _selector: Selector init(selector: @escaping Selector, observer: Observer, cancel: Cancelable) { _selector = selector super.init(maxConcurrent: 1, observer: observer, cancel: cancel) } override func performMap(_ element: SourceElement) throws -> SourceSequence { return try _selector(element) } } fileprivate final class MergeLimitedBasicSink: MergeLimitedSink where Observer.E == SourceSequence.E { override func performMap(_ element: SourceSequence) throws -> SourceSequence { return element } } fileprivate class MergeLimitedSink : Sink , ObserverType where Observer.E == SourceSequence.E { typealias QueueType = Queue let _maxConcurrent: Int let _lock = RecursiveLock() // state var _stopped = false var _activeCount = 0 var _queue = QueueType(capacity: 2) let _sourceSubscription = SingleAssignmentDisposable() let _group = CompositeDisposable() init(maxConcurrent: Int, observer: Observer, cancel: Cancelable) { _maxConcurrent = maxConcurrent let _ = _group.insert(_sourceSubscription) super.init(observer: observer, cancel: cancel) } func run(_ source: Observable) -> Disposable { let _ = _group.insert(_sourceSubscription) let disposable = source.subscribe(self) _sourceSubscription.setDisposable(disposable) return _group } func subscribe(_ innerSource: SourceSequence, group: CompositeDisposable) { let subscription = SingleAssignmentDisposable() let key = group.insert(subscription) if let key = key { let observer = MergeLimitedSinkIter(parent: self, disposeKey: key) let disposable = innerSource.asObservable().subscribe(observer) subscription.setDisposable(disposable) } } func performMap(_ element: SourceElement) throws -> SourceSequence { rxAbstractMethod() } @inline(__always) final private func nextElementArrived(element: SourceElement) -> SourceSequence? { _lock.lock(); defer { _lock.unlock() } // { let subscribe: Bool if _activeCount < _maxConcurrent { _activeCount += 1 subscribe = true } else { do { let value = try performMap(element) _queue.enqueue(value) } catch { forwardOn(.error(error)) dispose() } subscribe = false } if subscribe { do { return try performMap(element) } catch { forwardOn(.error(error)) dispose() } } return nil // } } func on(_ event: Event) { switch event { case .next(let element): if let sequence = self.nextElementArrived(element: element) { self.subscribe(sequence, group: _group) } case .error(let error): _lock.lock(); defer { _lock.unlock() } forwardOn(.error(error)) dispose() case .completed: _lock.lock(); defer { _lock.unlock() } if _activeCount == 0 { forwardOn(.completed) dispose() } else { _sourceSubscription.dispose() } _stopped = true } } } final fileprivate class MergeLimited : Producer { private let _source: Observable private let _maxConcurrent: Int init(source: Observable, maxConcurrent: Int) { _source = source _maxConcurrent = maxConcurrent } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == SourceSequence.E { let sink = MergeLimitedBasicSink(maxConcurrent: _maxConcurrent, observer: observer, cancel: cancel) let subscription = sink.run(_source) return (sink: sink, subscription: subscription) } } // MARK: Merge fileprivate final class MergeBasicSink : MergeSink where O.E == S.E { override func performMap(_ element: S) throws -> S { return element } } // MARK: flatMap fileprivate final class FlatMapSink : MergeSink where Observer.E == SourceSequence.E { typealias Selector = (SourceElement) throws -> SourceSequence private let _selector: Selector init(selector: @escaping Selector, observer: Observer, cancel: Cancelable) { _selector = selector super.init(observer: observer, cancel: cancel) } override func performMap(_ element: SourceElement) throws -> SourceSequence { return try _selector(element) } } // MARK: FlatMapFirst fileprivate final class FlatMapFirstSink : MergeSink where Observer.E == SourceSequence.E { typealias Selector = (SourceElement) throws -> SourceSequence private let _selector: Selector override var subscribeNext: Bool { return _activeCount == 0 } init(selector: @escaping Selector, observer: Observer, cancel: Cancelable) { _selector = selector super.init(observer: observer, cancel: cancel) } override func performMap(_ element: SourceElement) throws -> SourceSequence { return try _selector(element) } } fileprivate final class MergeSinkIter : ObserverType where Observer.E == SourceSequence.E { typealias Parent = MergeSink typealias DisposeKey = CompositeDisposable.DisposeKey typealias E = Observer.E private let _parent: Parent private let _disposeKey: DisposeKey init(parent: Parent, disposeKey: DisposeKey) { _parent = parent _disposeKey = disposeKey } func on(_ event: Event) { _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { switch event { case .next(let value): _parent.forwardOn(.next(value)) case .error(let error): _parent.forwardOn(.error(error)) _parent.dispose() case .completed: _parent._group.remove(for: _disposeKey) _parent._activeCount -= 1 _parent.checkCompleted() } // } } } fileprivate class MergeSink : Sink , ObserverType where Observer.E == SourceSequence.E { typealias ResultType = Observer.E typealias Element = SourceElement let _lock = RecursiveLock() var subscribeNext: Bool { return true } // state let _group = CompositeDisposable() let _sourceSubscription = SingleAssignmentDisposable() var _activeCount = 0 var _stopped = false override init(observer: Observer, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } func performMap(_ element: SourceElement) throws -> SourceSequence { rxAbstractMethod() } @inline(__always) final private func nextElementArrived(element: SourceElement) -> SourceSequence? { _lock.lock(); defer { _lock.unlock() } // { if !subscribeNext { return nil } do { let value = try performMap(element) _activeCount += 1 return value } catch let e { forwardOn(.error(e)) dispose() return nil } // } } func on(_ event: Event) { switch event { case .next(let element): if let value = nextElementArrived(element: element) { subscribeInner(value.asObservable()) } case .error(let error): _lock.lock(); defer { _lock.unlock() } forwardOn(.error(error)) dispose() case .completed: _lock.lock(); defer { _lock.unlock() } _stopped = true _sourceSubscription.dispose() checkCompleted() } } func subscribeInner(_ source: Observable) { let iterDisposable = SingleAssignmentDisposable() if let disposeKey = _group.insert(iterDisposable) { let iter = MergeSinkIter(parent: self, disposeKey: disposeKey) let subscription = source.subscribe(iter) iterDisposable.setDisposable(subscription) } } func run(_ sources: [Observable]) -> Disposable { _activeCount += sources.count for source in sources { subscribeInner(source) } _stopped = true checkCompleted() return _group } @inline(__always) func checkCompleted() { if _stopped && _activeCount == 0 { self.forwardOn(.completed) self.dispose() } } func run(_ source: Observable) -> Disposable { let _ = _group.insert(_sourceSubscription) let subscription = source.subscribe(self) _sourceSubscription.setDisposable(subscription) return _group } } // MARK: Producers final fileprivate class FlatMap: Producer { typealias Selector = (SourceElement) throws -> SourceSequence private let _source: Observable private let _selector: Selector init(source: Observable, selector: @escaping Selector) { _source = source _selector = selector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == SourceSequence.E { let sink = FlatMapSink(selector: _selector, observer: observer, cancel: cancel) let subscription = sink.run(_source) return (sink: sink, subscription: subscription) } } final fileprivate class FlatMapFirst: Producer { typealias Selector = (SourceElement) throws -> SourceSequence private let _source: Observable private let _selector: Selector init(source: Observable, selector: @escaping Selector) { _source = source _selector = selector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == SourceSequence.E { let sink = FlatMapFirstSink(selector: _selector, observer: observer, cancel: cancel) let subscription = sink.run(_source) return (sink: sink, subscription: subscription) } } final class ConcatMap: Producer { typealias Selector = (SourceElement) throws -> SourceSequence private let _source: Observable private let _selector: Selector init(source: Observable, selector: @escaping Selector) { _source = source _selector = selector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == SourceSequence.E { let sink = ConcatMapSink(selector: _selector, observer: observer, cancel: cancel) let subscription = sink.run(_source) return (sink: sink, subscription: subscription) } } final class Merge : Producer { private let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == SourceSequence.E { let sink = MergeBasicSink(observer: observer, cancel: cancel) let subscription = sink.run(_source) return (sink: sink, subscription: subscription) } } final fileprivate class MergeArray : Producer { private let _sources: [Observable] init(sources: [Observable]) { _sources = sources } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = MergeBasicSink, O>(observer: observer, cancel: cancel) let subscription = sink.run(_sources) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Multicast.swift ================================================ // // Multicast.swift // RxSwift // // Created by Krunoslav Zaher on 2/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /** Represents an observable wrapper that can be connected and disconnected from its underlying observable sequence. */ public class ConnectableObservable : Observable , ConnectableObservableType { /** Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established. - returns: Disposable used to disconnect the observable wrapper from its source, causing subscribed observer to stop receiving values from the underlying observable sequence. */ public func connect() -> Disposable { rxAbstractMethod() } } extension ObservableType { /** Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a selector function. Each subscription to the resulting sequence causes a separate multicast invocation, exposing the sequence resulting from the selector function's invocation. For specializations with fixed subject types, see `publish` and `replay`. - seealso: [multicast operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html) - parameter subjectSelector: Factory function to create an intermediate subject through which the source sequence's elements will be multicast to the selector function. - parameter selector: Selector function which can use the multicasted source sequence subject to the policies enforced by the created subject. - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function. */ public func multicast(_ subjectSelector: @escaping () throws -> S, selector: @escaping (Observable) throws -> Observable) -> Observable where S.SubjectObserverType.E == E { return Multicast( source: self.asObservable(), subjectSelector: subjectSelector, selector: selector ) } } extension ObservableType { /** Returns a connectable observable sequence that shares a single subscription to the underlying sequence. This operator is a specialization of `multicast` using a `PublishSubject`. - seealso: [publish operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html) - returns: A connectable observable sequence that shares a single subscription to the underlying sequence. */ public func publish() -> ConnectableObservable { return self.multicast { PublishSubject() } } } extension ObservableType { /** Returns a connectable observable sequence that shares a single subscription to the underlying sequence replaying bufferSize elements. This operator is a specialization of `multicast` using a `ReplaySubject`. - seealso: [replay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html) - parameter bufferSize: Maximum element count of the replay buffer. - returns: A connectable observable sequence that shares a single subscription to the underlying sequence. */ public func replay(_ bufferSize: Int) -> ConnectableObservable { return self.multicast { ReplaySubject.create(bufferSize: bufferSize) } } /** Returns a connectable observable sequence that shares a single subscription to the underlying sequence replaying all elements. This operator is a specialization of `multicast` using a `ReplaySubject`. - seealso: [replay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html) - returns: A connectable observable sequence that shares a single subscription to the underlying sequence. */ public func replayAll() -> ConnectableObservable { return self.multicast { ReplaySubject.createUnbounded() } } } extension ConnectableObservableType { /** Returns an observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence. - seealso: [refCount operator on reactivex.io](http://reactivex.io/documentation/operators/refcount.html) - returns: An observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence. */ public func refCount() -> Observable { return RefCount(source: self) } } extension ObservableType { /** Multicasts the source sequence notifications through the specified subject to the resulting connectable observable. Upon connection of the connectable observable, the subject is subscribed to the source exactly one, and messages are forwarded to the observers registered with the connectable observable. For specializations with fixed subject types, see `publish` and `replay`. - seealso: [multicast operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html) - parameter subject: Subject to push source elements into. - returns: A connectable observable sequence that upon connection causes the source sequence to push results into the specified subject. */ public func multicast(_ subject: S) -> ConnectableObservable where S.SubjectObserverType.E == E { return ConnectableObservableAdapter(source: self.asObservable(), makeSubject: { subject }) } /** Multicasts the source sequence notifications through an instantiated subject to the resulting connectable observable. Upon connection of the connectable observable, the subject is subscribed to the source exactly one, and messages are forwarded to the observers registered with the connectable observable. Subject is cleared on connection disposal or in case source sequence produces terminal event. - seealso: [multicast operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html) - parameter makeSubject: Factory function used to instantiate a subject for each connection. - returns: A connectable observable sequence that upon connection causes the source sequence to push results into the specified subject. */ public func multicast(makeSubject: @escaping () -> S) -> ConnectableObservable where S.SubjectObserverType.E == E { return ConnectableObservableAdapter(source: self.asObservable(), makeSubject: makeSubject) } } final fileprivate class Connection : ObserverType, Disposable { typealias E = S.SubjectObserverType.E private var _lock: RecursiveLock // state private var _parent: ConnectableObservableAdapter? private var _subscription : Disposable? private var _subjectObserver: S.SubjectObserverType private var _disposed: Bool = false init(parent: ConnectableObservableAdapter, subjectObserver: S.SubjectObserverType, lock: RecursiveLock, subscription: Disposable) { _parent = parent _subscription = subscription _lock = lock _subjectObserver = subjectObserver } func on(_ event: Event) { if _disposed { return } if event.isStopEvent { self.dispose() } _subjectObserver.on(event) } func dispose() { _lock.lock(); defer { _lock.unlock() } // { _disposed = true guard let parent = _parent else { return } if parent._connection === self { parent._connection = nil parent._subject = nil } _parent = nil _subscription?.dispose() _subscription = nil // } } } final fileprivate class ConnectableObservableAdapter : ConnectableObservable { typealias ConnectionType = Connection fileprivate let _source: Observable fileprivate let _makeSubject: () -> S fileprivate let _lock = RecursiveLock() fileprivate var _subject: S? // state fileprivate var _connection: ConnectionType? init(source: Observable, makeSubject: @escaping () -> S) { _source = source _makeSubject = makeSubject _subject = nil _connection = nil } override func connect() -> Disposable { return _lock.calculateLocked { if let connection = _connection { return connection } let singleAssignmentDisposable = SingleAssignmentDisposable() let connection = Connection(parent: self, subjectObserver: self.lazySubject.asObserver(), lock: _lock, subscription: singleAssignmentDisposable) _connection = connection let subscription = _source.subscribe(connection) singleAssignmentDisposable.setDisposable(subscription) return connection } } fileprivate var lazySubject: S { if let subject = self._subject { return subject } let subject = _makeSubject() self._subject = subject return subject } override func subscribe(_ observer: O) -> Disposable where O.E == S.E { return self.lazySubject.subscribe(observer) } } final fileprivate class RefCountSink : Sink , ObserverType where CO.E == O.E { typealias Element = O.E typealias Parent = RefCount private let _parent: Parent private var _connectionIdSnapshot: Int64 = -1 init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let subscription = _parent._source.subscribe(self) _parent._lock.lock(); defer { _parent._lock.unlock() } // { _connectionIdSnapshot = _parent._connectionId if self.disposed { return Disposables.create() } if _parent._count == 0 { _parent._count = 1 _parent._connectableSubscription = _parent._source.connect() } else { _parent._count = _parent._count + 1 } // } return Disposables.create { subscription.dispose() self._parent._lock.lock(); defer { self._parent._lock.unlock() } // { if self._parent._connectionId != self._connectionIdSnapshot { return } if self._parent._count == 1 { self._parent._count = 0 guard let connectableSubscription = self._parent._connectableSubscription else { return } connectableSubscription.dispose() self._parent._connectableSubscription = nil } else if self._parent._count > 1 { self._parent._count = self._parent._count - 1 } else { rxFatalError("Something went wrong with RefCount disposing mechanism") } // } } } func on(_ event: Event) { switch event { case .next: forwardOn(event) case .error, .completed: _parent._lock.lock() // { if _parent._connectionId == self._connectionIdSnapshot { let connection = _parent._connectableSubscription defer { connection?.dispose() } _parent._count = 0 _parent._connectionId = _parent._connectionId &+ 1 _parent._connectableSubscription = nil } // } _parent._lock.unlock() forwardOn(event) dispose() } } } final fileprivate class RefCount: Producer { fileprivate let _lock = RecursiveLock() // state fileprivate var _count = 0 fileprivate var _connectionId: Int64 = 0 fileprivate var _connectableSubscription = nil as Disposable? fileprivate let _source: CO init(source: CO) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == CO.E { let sink = RefCountSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } final fileprivate class MulticastSink: Sink, ObserverType { typealias Element = O.E typealias ResultType = Element typealias MutlicastType = Multicast private let _parent: MutlicastType init(parent: MutlicastType, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { do { let subject = try _parent._subjectSelector() let connectable = ConnectableObservableAdapter(source: _parent._source, makeSubject: { subject }) let observable = try _parent._selector(connectable) let subscription = observable.subscribe(self) let connection = connectable.connect() return Disposables.create(subscription, connection) } catch let e { forwardOn(.error(e)) dispose() return Disposables.create() } } func on(_ event: Event) { forwardOn(event) switch event { case .next: break case .error, .completed: dispose() } } } final fileprivate class Multicast: Producer { typealias SubjectSelectorType = () throws -> S typealias SelectorType = (Observable) throws -> Observable fileprivate let _source: Observable fileprivate let _subjectSelector: SubjectSelectorType fileprivate let _selector: SelectorType init(source: Observable, subjectSelector: @escaping SubjectSelectorType, selector: @escaping SelectorType) { _source = source _subjectSelector = subjectSelector _selector = selector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = MulticastSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Never.swift ================================================ // // Never.swift // RxSwift // // Created by Krunoslav Zaher on 8/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns a non-terminating observable sequence, which can be used to denote an infinite duration. - seealso: [never operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html) - returns: An observable sequence whose observers will never get called. */ public static func never() -> Observable { return NeverProducer() } } final fileprivate class NeverProducer : Producer { override func subscribe(_ observer: O) -> Disposable where O.E == Element { return Disposables.create() } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/ObserveOn.swift ================================================ // // ObserveOn.swift // RxSwift // // Created by Krunoslav Zaher on 7/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Wraps the source sequence in order to run its observer callbacks on the specified scheduler. This only invokes observer callbacks on a `scheduler`. In case the subscription and/or unsubscription actions have side-effects that require to be run on a scheduler, use `subscribeOn`. - seealso: [observeOn operator on reactivex.io](http://reactivex.io/documentation/operators/observeon.html) - parameter scheduler: Scheduler to notify observers on. - returns: The source sequence whose observations happen on the specified scheduler. */ public func observeOn(_ scheduler: ImmediateSchedulerType) -> Observable { if let scheduler = scheduler as? SerialDispatchQueueScheduler { return ObserveOnSerialDispatchQueue(source: self.asObservable(), scheduler: scheduler) } else { return ObserveOn(source: self.asObservable(), scheduler: scheduler) } } } final fileprivate class ObserveOn : Producer { let scheduler: ImmediateSchedulerType let source: Observable init(source: Observable, scheduler: ImmediateSchedulerType) { self.scheduler = scheduler self.source = source #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = ObserveOnSink(scheduler: scheduler, observer: observer, cancel: cancel) let subscription = source.subscribe(sink) return (sink: sink, subscription: subscription) } #if TRACE_RESOURCES deinit { let _ = Resources.decrementTotal() } #endif } enum ObserveOnState : Int32 { // pump is not running case stopped = 0 // pump is running case running = 1 } final fileprivate class ObserveOnSink : ObserverBase { typealias E = O.E let _scheduler: ImmediateSchedulerType var _lock = SpinLock() let _observer: O // state var _state = ObserveOnState.stopped var _queue = Queue>(capacity: 10) let _scheduleDisposable = SerialDisposable() let _cancel: Cancelable init(scheduler: ImmediateSchedulerType, observer: O, cancel: Cancelable) { _scheduler = scheduler _observer = observer _cancel = cancel } override func onCore(_ event: Event) { let shouldStart = _lock.calculateLocked { () -> Bool in self._queue.enqueue(event) switch self._state { case .stopped: self._state = .running return true case .running: return false } } if shouldStart { _scheduleDisposable.disposable = self._scheduler.scheduleRecursive((), action: self.run) } } func run(_ state: (), _ recurse: (()) -> ()) { let (nextEvent, observer) = self._lock.calculateLocked { () -> (Event?, O) in if self._queue.count > 0 { return (self._queue.dequeue(), self._observer) } else { self._state = .stopped return (nil, self._observer) } } if let nextEvent = nextEvent, !_cancel.isDisposed { observer.on(nextEvent) if nextEvent.isStopEvent { dispose() } } else { return } let shouldContinue = _shouldContinue_synchronized() if shouldContinue { recurse(()) } } func _shouldContinue_synchronized() -> Bool { _lock.lock(); defer { _lock.unlock() } // { if self._queue.count > 0 { return true } else { self._state = .stopped return false } // } } override func dispose() { super.dispose() _cancel.dispose() _scheduleDisposable.dispose() } } #if TRACE_RESOURCES fileprivate var _numberOfSerialDispatchQueueObservables: AtomicInt = 0 extension Resources { /** Counts number of `SerialDispatchQueueObservables`. Purposed for unit tests. */ public static var numberOfSerialDispatchQueueObservables: Int32 { return _numberOfSerialDispatchQueueObservables.valueSnapshot() } } #endif final fileprivate class ObserveOnSerialDispatchQueueSink : ObserverBase { let scheduler: SerialDispatchQueueScheduler let observer: O let cancel: Cancelable var cachedScheduleLambda: (((sink: ObserveOnSerialDispatchQueueSink, event: Event)) -> Disposable)! init(scheduler: SerialDispatchQueueScheduler, observer: O, cancel: Cancelable) { self.scheduler = scheduler self.observer = observer self.cancel = cancel super.init() cachedScheduleLambda = { pair in pair.sink.observer.on(pair.event) if pair.event.isStopEvent { pair.sink.dispose() } return Disposables.create() } } override func onCore(_ event: Event) { let _ = self.scheduler.schedule((self, event), action: cachedScheduleLambda!) } override func dispose() { super.dispose() cancel.dispose() } } final fileprivate class ObserveOnSerialDispatchQueue : Producer { let scheduler: SerialDispatchQueueScheduler let source: Observable init(source: Observable, scheduler: SerialDispatchQueueScheduler) { self.scheduler = scheduler self.source = source #if TRACE_RESOURCES let _ = Resources.incrementTotal() let _ = AtomicIncrement(&_numberOfSerialDispatchQueueObservables) #endif } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = ObserveOnSerialDispatchQueueSink(scheduler: scheduler, observer: observer, cancel: cancel) let subscription = source.subscribe(sink) return (sink: sink, subscription: subscription) } #if TRACE_RESOURCES deinit { let _ = Resources.decrementTotal() let _ = AtomicDecrement(&_numberOfSerialDispatchQueueObservables) } #endif } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Optional.swift ================================================ // // Optional.swift // RxSwift // // Created by tarunon on 2016/12/13. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Converts a optional to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - parameter optional: Optional element in the resulting observable sequence. - returns: An observable sequence containing the wrapped value or not from given optional. */ public static func from(optional: E?) -> Observable { return ObservableOptional(optional: optional) } /** Converts a optional to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - parameter optional: Optional element in the resulting observable sequence. - parameter: Scheduler to send the optional element on. - returns: An observable sequence containing the wrapped value or not from given optional. */ public static func from(optional: E?, scheduler: ImmediateSchedulerType) -> Observable { return ObservableOptionalScheduled(optional: optional, scheduler: scheduler) } } final fileprivate class ObservableOptionalScheduledSink : Sink { typealias E = O.E typealias Parent = ObservableOptionalScheduled private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { return _parent._scheduler.schedule(_parent._optional) { (optional: E?) -> Disposable in if let next = optional { self.forwardOn(.next(next)) return self._parent._scheduler.schedule(()) { _ in self.forwardOn(.completed) self.dispose() return Disposables.create() } } else { self.forwardOn(.completed) self.dispose() return Disposables.create() } } } } final fileprivate class ObservableOptionalScheduled : Producer { fileprivate let _optional: E? fileprivate let _scheduler: ImmediateSchedulerType init(optional: E?, scheduler: ImmediateSchedulerType) { _optional = optional _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = ObservableOptionalScheduledSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } final fileprivate class ObservableOptional: Producer { private let _optional: E? init(optional: E?) { _optional = optional } override func subscribe(_ observer: O) -> Disposable where O.E == E { if let element = _optional { observer.on(.next(element)) } observer.on(.completed) return Disposables.create() } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Producer.swift ================================================ // // Producer.swift // RxSwift // // Created by Krunoslav Zaher on 2/20/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // class Producer : Observable { override init() { super.init() } override func subscribe(_ observer: O) -> Disposable where O.E == Element { if !CurrentThreadScheduler.isScheduleRequired { // The returned disposable needs to release all references once it was disposed. let disposer = SinkDisposer() let sinkAndSubscription = run(observer, cancel: disposer) disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription) return disposer } else { return CurrentThreadScheduler.instance.schedule(()) { _ in let disposer = SinkDisposer() let sinkAndSubscription = self.run(observer, cancel: disposer) disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription) return disposer } } } func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { rxAbstractMethod() } } fileprivate final class SinkDisposer: Cancelable { fileprivate enum DisposeState: UInt32 { case disposed = 1 case sinkAndSubscriptionSet = 2 } // Jeej, swift API consistency rules fileprivate enum DisposeStateInt32: Int32 { case disposed = 1 case sinkAndSubscriptionSet = 2 } private var _state: AtomicInt = 0 private var _sink: Disposable? = nil private var _subscription: Disposable? = nil var isDisposed: Bool { return AtomicFlagSet(DisposeState.disposed.rawValue, &_state) } func setSinkAndSubscription(sink: Disposable, subscription: Disposable) { _sink = sink _subscription = subscription let previousState = AtomicOr(DisposeState.sinkAndSubscriptionSet.rawValue, &_state) if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 { rxFatalError("Sink and subscription were already set") } if (previousState & DisposeStateInt32.disposed.rawValue) != 0 { sink.dispose() subscription.dispose() _sink = nil _subscription = nil } } func dispose() { let previousState = AtomicOr(DisposeState.disposed.rawValue, &_state) if (previousState & DisposeStateInt32.disposed.rawValue) != 0 { return } if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 { guard let sink = _sink else { rxFatalError("Sink not set") } guard let subscription = _subscription else { rxFatalError("Subscription not set") } sink.dispose() subscription.dispose() _sink = nil _subscription = nil } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Range.swift ================================================ // // Range.swift // RxSwift // // Created by Krunoslav Zaher on 9/13/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType where E : RxAbstractInteger { /** Generates an observable sequence of integral numbers within a specified range, using the specified scheduler to generate and send out observer messages. - seealso: [range operator on reactivex.io](http://reactivex.io/documentation/operators/range.html) - parameter start: The value of the first integer in the sequence. - parameter count: The number of sequential integers to generate. - parameter scheduler: Scheduler to run the generator loop on. - returns: An observable sequence that contains a range of sequential integral numbers. */ public static func range(start: E, count: E, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable { return RangeProducer(start: start, count: count, scheduler: scheduler) } } final fileprivate class RangeProducer : Producer { fileprivate let _start: E fileprivate let _count: E fileprivate let _scheduler: ImmediateSchedulerType init(start: E, count: E, scheduler: ImmediateSchedulerType) { if count < 0 { rxFatalError("count can't be negative") } if start &+ (count - 1) < start { rxFatalError("overflow of count") } _start = start _count = count _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = RangeSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } final fileprivate class RangeSink : Sink where O.E: RxAbstractInteger { typealias Parent = RangeProducer private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { return _parent._scheduler.scheduleRecursive(0 as O.E) { i, recurse in if i < self._parent._count { self.forwardOn(.next(self._parent._start + i)) recurse(i + 1) } else { self.forwardOn(.completed) self.dispose() } } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Reduce.swift ================================================ // // Reduce.swift // RxSwift // // Created by Krunoslav Zaher on 4/1/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Applies an `accumulator` function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified `seed` value is used as the initial accumulator value. For aggregation behavior with incremental intermediate results, see `scan`. - seealso: [reduce operator on reactivex.io](http://reactivex.io/documentation/operators/reduce.html) - parameter seed: The initial accumulator value. - parameter accumulator: A accumulator function to be invoked on each element. - parameter mapResult: A function to transform the final accumulator value into the result value. - returns: An observable sequence containing a single element with the final accumulator value. */ public func reduce(_ seed: A, accumulator: @escaping (A, E) throws -> A, mapResult: @escaping (A) throws -> R) -> Observable { return Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: mapResult) } /** Applies an `accumulator` function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified `seed` value is used as the initial accumulator value. For aggregation behavior with incremental intermediate results, see `scan`. - seealso: [reduce operator on reactivex.io](http://reactivex.io/documentation/operators/reduce.html) - parameter seed: The initial accumulator value. - parameter accumulator: A accumulator function to be invoked on each element. - returns: An observable sequence containing a single element with the final accumulator value. */ public func reduce
(_ seed: A, accumulator: @escaping (A, E) throws -> A) -> Observable { return Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: { $0 }) } } final fileprivate class ReduceSink : Sink, ObserverType { typealias ResultType = O.E typealias Parent = Reduce private let _parent: Parent private var _accumulation: AccumulateType init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _accumulation = parent._seed super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): do { _accumulation = try _parent._accumulator(_accumulation, value) } catch let e { forwardOn(.error(e)) dispose() } case .error(let e): forwardOn(.error(e)) dispose() case .completed: do { let result = try _parent._mapResult(_accumulation) forwardOn(.next(result)) forwardOn(.completed) dispose() } catch let e { forwardOn(.error(e)) dispose() } } } } final fileprivate class Reduce : Producer { typealias AccumulatorType = (AccumulateType, SourceType) throws -> AccumulateType typealias ResultSelectorType = (AccumulateType) throws -> ResultType fileprivate let _source: Observable fileprivate let _seed: AccumulateType fileprivate let _accumulator: AccumulatorType fileprivate let _mapResult: ResultSelectorType init(source: Observable, seed: AccumulateType, accumulator: @escaping AccumulatorType, mapResult: @escaping ResultSelectorType) { _source = source _seed = seed _accumulator = accumulator _mapResult = mapResult } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == ResultType { let sink = ReduceSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Repeat.swift ================================================ // // Repeat.swift // RxSwift // // Created by Krunoslav Zaher on 9/13/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Generates an observable sequence that repeats the given element infinitely, using the specified scheduler to send out observer messages. - seealso: [repeat operator on reactivex.io](http://reactivex.io/documentation/operators/repeat.html) - parameter element: Element to repeat. - parameter scheduler: Scheduler to run the producer loop on. - returns: An observable sequence that repeats the given element infinitely. */ public static func repeatElement(_ element: E, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable { return RepeatElement(element: element, scheduler: scheduler) } } final fileprivate class RepeatElement : Producer { fileprivate let _element: Element fileprivate let _scheduler: ImmediateSchedulerType init(element: Element, scheduler: ImmediateSchedulerType) { _element = element _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = RepeatElementSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } final fileprivate class RepeatElementSink : Sink { typealias Parent = RepeatElement private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { return _parent._scheduler.scheduleRecursive(_parent._element) { e, recurse in self.forwardOn(.next(e)) recurse(e) } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/RetryWhen.swift ================================================ // // RetryWhen.swift // RxSwift // // Created by Junior B. on 06/10/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Repeats the source observable sequence on error when the notifier emits a next value. If the source observable errors and the notifier completes, it will complete the source sequence. - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html) - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable. - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. */ public func retryWhen(_ notificationHandler: @escaping (Observable) -> TriggerObservable) -> Observable { return RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler) } /** Repeats the source observable sequence on error when the notifier emits a next value. If the source observable errors and the notifier completes, it will complete the source sequence. - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html) - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable. - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. */ public func retryWhen(_ notificationHandler: @escaping (Observable) -> TriggerObservable) -> Observable { return RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler) } } final fileprivate class RetryTriggerSink : ObserverType where S.Iterator.Element : ObservableType, S.Iterator.Element.E == O.E { typealias E = TriggerObservable.E typealias Parent = RetryWhenSequenceSinkIter fileprivate let _parent: Parent init(parent: Parent) { _parent = parent } func on(_ event: Event) { switch event { case .next: _parent._parent._lastError = nil _parent._parent.schedule(.moveNext) case .error(let e): _parent._parent.forwardOn(.error(e)) _parent._parent.dispose() case .completed: _parent._parent.forwardOn(.completed) _parent._parent.dispose() } } } final fileprivate class RetryWhenSequenceSinkIter : ObserverType , Disposable where S.Iterator.Element : ObservableType, S.Iterator.Element.E == O.E { typealias E = O.E typealias Parent = RetryWhenSequenceSink fileprivate let _parent: Parent fileprivate let _errorHandlerSubscription = SingleAssignmentDisposable() fileprivate let _subscription: Disposable init(parent: Parent, subscription: Disposable) { _parent = parent _subscription = subscription } func on(_ event: Event) { switch event { case .next: _parent.forwardOn(event) case .error(let error): _parent._lastError = error if let failedWith = error as? Error { // dispose current subscription _subscription.dispose() let errorHandlerSubscription = _parent._notifier.subscribe(RetryTriggerSink(parent: self)) _errorHandlerSubscription.setDisposable(errorHandlerSubscription) _parent._errorSubject.on(.next(failedWith)) } else { _parent.forwardOn(.error(error)) _parent.dispose() } case .completed: _parent.forwardOn(event) _parent.dispose() } } final func dispose() { _subscription.dispose() _errorHandlerSubscription.dispose() } } final fileprivate class RetryWhenSequenceSink : TailRecursiveSink where S.Iterator.Element : ObservableType, S.Iterator.Element.E == O.E { typealias Element = O.E typealias Parent = RetryWhenSequence let _lock = RecursiveLock() fileprivate let _parent: Parent fileprivate var _lastError: Swift.Error? fileprivate let _errorSubject = PublishSubject() fileprivate let _handler: Observable fileprivate let _notifier = PublishSubject() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _handler = parent._notificationHandler(_errorSubject).asObservable() super.init(observer: observer, cancel: cancel) } override func done() { if let lastError = _lastError { forwardOn(.error(lastError)) _lastError = nil } else { forwardOn(.completed) } dispose() } override func extract(_ observable: Observable) -> SequenceGenerator? { // It is important to always return `nil` here because there are sideffects in the `run` method // that are dependant on particular `retryWhen` operator so single operator stack can't be reused in this // case. return nil } override func subscribeToNext(_ source: Observable) -> Disposable { let subscription = SingleAssignmentDisposable() let iter = RetryWhenSequenceSinkIter(parent: self, subscription: subscription) subscription.setDisposable(source.subscribe(iter)) return iter } override func run(_ sources: SequenceGenerator) -> Disposable { let triggerSubscription = _handler.subscribe(_notifier.asObserver()) let superSubscription = super.run(sources) return Disposables.create(superSubscription, triggerSubscription) } } final fileprivate class RetryWhenSequence : Producer where S.Iterator.Element : ObservableType { typealias Element = S.Iterator.Element.E fileprivate let _sources: S fileprivate let _notificationHandler: (Observable) -> TriggerObservable init(sources: S, notificationHandler: @escaping (Observable) -> TriggerObservable) { _sources = sources _notificationHandler = notificationHandler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = RetryWhenSequenceSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run((self._sources.makeIterator(), nil)) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Sample.swift ================================================ // // Sample.swift // RxSwift // // Created by Krunoslav Zaher on 5/1/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Samples the source observable sequence using a sampler observable sequence producing sampling ticks. Upon each sampling tick, the latest element (if any) in the source sequence during the last sampling interval is sent to the resulting sequence. **In case there were no new elements between sampler ticks, no element is sent to the resulting sequence.** - seealso: [sample operator on reactivex.io](http://reactivex.io/documentation/operators/sample.html) - parameter sampler: Sampling tick sequence. - returns: Sampled observable sequence. */ public func sample(_ sampler: O) -> Observable { return Sample(source: self.asObservable(), sampler: sampler.asObservable()) } } final fileprivate class SamplerSink : ObserverType , LockOwnerType , SynchronizedOnType { typealias E = SampleType typealias Parent = SampleSequenceSink fileprivate let _parent: Parent var _lock: RecursiveLock { return _parent._lock } init(parent: Parent) { _parent = parent } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next: if let element = _parent._element { _parent._element = nil _parent.forwardOn(.next(element)) } if _parent._atEnd { _parent.forwardOn(.completed) _parent.dispose() } case .error(let e): _parent.forwardOn(.error(e)) _parent.dispose() case .completed: if let element = _parent._element { _parent._element = nil _parent.forwardOn(.next(element)) } if _parent._atEnd { _parent.forwardOn(.completed) _parent.dispose() } } } } final fileprivate class SampleSequenceSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType { typealias Element = O.E typealias Parent = Sample fileprivate let _parent: Parent let _lock = RecursiveLock() // state fileprivate var _element = nil as Element? fileprivate var _atEnd = false fileprivate let _sourceSubscription = SingleAssignmentDisposable() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { _sourceSubscription.setDisposable(_parent._source.subscribe(self)) let samplerSubscription = _parent._sampler.subscribe(SamplerSink(parent: self)) return Disposables.create(_sourceSubscription, samplerSubscription) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next(let element): _element = element case .error: forwardOn(event) dispose() case .completed: _atEnd = true _sourceSubscription.dispose() } } } final fileprivate class Sample : Producer { fileprivate let _source: Observable fileprivate let _sampler: Observable init(source: Observable, sampler: Observable) { _source = source _sampler = sampler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = SampleSequenceSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Scan.swift ================================================ // // Scan.swift // RxSwift // // Created by Krunoslav Zaher on 6/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value. For aggregation behavior with no intermediate results, see `reduce`. - seealso: [scan operator on reactivex.io](http://reactivex.io/documentation/operators/scan.html) - parameter seed: The initial accumulator value. - parameter accumulator: An accumulator function to be invoked on each element. - returns: An observable sequence containing the accumulated values. */ public func scan(_ seed: A, accumulator: @escaping (A, E) throws -> A) -> Observable { return Scan(source: self.asObservable(), seed: seed, accumulator: accumulator) } } final fileprivate class ScanSink : Sink, ObserverType { typealias Accumulate = O.E typealias Parent = Scan typealias E = ElementType fileprivate let _parent: Parent fileprivate var _accumulate: Accumulate init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _accumulate = parent._seed super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let element): do { _accumulate = try _parent._accumulator(_accumulate, element) forwardOn(.next(_accumulate)) } catch let error { forwardOn(.error(error)) dispose() } case .error(let error): forwardOn(.error(error)) dispose() case .completed: forwardOn(.completed) dispose() } } } final fileprivate class Scan: Producer { typealias Accumulator = (Accumulate, Element) throws -> Accumulate fileprivate let _source: Observable fileprivate let _seed: Accumulate fileprivate let _accumulator: Accumulator init(source: Observable, seed: Accumulate, accumulator: @escaping Accumulator) { _source = source _seed = seed _accumulator = accumulator } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Accumulate { let sink = ScanSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Sequence.swift ================================================ // // Sequence.swift // RxSwift // // Created by Krunoslav Zaher on 11/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { // MARK: of /** This method creates a new Observable instance with a variable number of elements. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - parameter elements: Elements to generate. - parameter scheduler: Scheduler to send elements on. If `nil`, elements are sent immediately on subscription. - returns: The observable sequence whose elements are pulled from the given arguments. */ public static func of(_ elements: E ..., scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable { return ObservableSequence(elements: elements, scheduler: scheduler) } } extension ObservableType { /** Converts an array to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - returns: The observable sequence whose elements are pulled from the given enumerable sequence. */ public static func from(_ array: [E], scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable { return ObservableSequence(elements: array, scheduler: scheduler) } /** Converts a sequence to an observable sequence. - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html) - returns: The observable sequence whose elements are pulled from the given enumerable sequence. */ public static func from(_ sequence: S, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable where S.Iterator.Element == E { return ObservableSequence(elements: sequence, scheduler: scheduler) } } final fileprivate class ObservableSequenceSink : Sink where S.Iterator.Element == O.E { typealias Parent = ObservableSequence private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { return _parent._scheduler.scheduleRecursive((_parent._elements.makeIterator(), _parent._elements)) { (iterator, recurse) in var mutableIterator = iterator if let next = mutableIterator.0.next() { self.forwardOn(.next(next)) recurse(mutableIterator) } else { self.forwardOn(.completed) self.dispose() } } } } final fileprivate class ObservableSequence : Producer { fileprivate let _elements: S fileprivate let _scheduler: ImmediateSchedulerType init(elements: S, scheduler: ImmediateSchedulerType) { _elements = elements _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = ObservableSequenceSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/ShareReplayScope.swift ================================================ // // ShareReplayScope.swift // RxSwift // // Created by Krunoslav Zaher on 5/28/17. // Copyright © 2017 Krunoslav Zaher. All rights reserved. // /// Subject lifetime scope public enum SubjectLifetimeScope { /** **Each connection will have it's own subject instance to store replay events.** **Connections will be isolated from each another.** Configures the underlying implementation to behave equivalent to. ``` source.multicast(makeSubject: { MySubject() }).refCount() ``` **This is the recommended default.** This has the following consequences: * `retry` or `concat` operators will function as expected because terminating the sequence will clear internal state. * Each connection to source observable sequence will use it's own subject. * When the number of subscribers drops from 1 to 0 and connection to source sequence is disposed, subject will be cleared. ``` let xs = Observable.deferred { () -> Observable in print("Performing work ...") return Observable.just(Date().timeIntervalSince1970) } .share(replay: 1, scope: .whileConnected) _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") }) _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") }) _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") }) ``` Notice how time interval is different and `Performing work ...` is printed each time) ``` Performing work ... next 1495998900.82141 completed Performing work ... next 1495998900.82359 completed Performing work ... next 1495998900.82444 completed ``` */ case whileConnected /** **One subject will store replay events for all connections to source.** **Connections won't be isolated from each another.** Configures the underlying implementation behave equivalent to. ``` source.multicast(MySubject()).refCount() ``` This has the following consequences: * Using `retry` or `concat` operators after this operator usually isn't advised. * Each connection to source observable sequence will share the same subject. * After number of subscribers drops from 1 to 0 and connection to source observable sequence is dispose, this operator will continue holding a reference to the same subject. If at some later moment a new observer initiates a new connection to source it can potentially receive some of the stale events received during previous connection. * After source sequence terminates any new observer will always immediatelly receive replayed elements and terminal event. No new subscriptions to source observable sequence will be attempted. ``` let xs = Observable.deferred { () -> Observable in print("Performing work ...") return Observable.just(Date().timeIntervalSince1970) } .share(replay: 1, scope: .forever) _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") }) _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") }) _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") }) ``` Notice how time interval is the same, replayed, and `Performing work ...` is printed only once ``` Performing work ... next 1495999013.76356 completed next 1495999013.76356 completed next 1495999013.76356 completed ``` */ case forever } extension ObservableType { /** Returns an observable sequence that **shares a single subscription to the underlying sequence**, and immediately upon subscription replays elements in buffer. This operator is equivalent to: * `.whileConnected` ``` // Each connection will have it's own subject instance to store replay events. // Connections will be isolated from each another. source.multicast(makeSubject: { Replay.create(bufferSize: replay) }).refCount() ``` * `.forever` ``` // One subject will store replay events for all connections to source. // Connections won't be isolated from each another. source.multicast(Replay.create(bufferSize: replay)).refCount() ``` It uses optimized versions of the operators for most common operations. - parameter replay: Maximum element count of the replay buffer. - parameter scope: Lifetime scope of sharing subject. For more information see `SubjectLifetimeScope` enum. - seealso: [shareReplay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html) - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence. */ public func share(replay: Int = 0, scope: SubjectLifetimeScope = .whileConnected) -> Observable { switch scope { case .forever: switch replay { case 0: return self.multicast(PublishSubject()).refCount() default: return self.multicast(ReplaySubject.create(bufferSize: replay)).refCount() } case .whileConnected: switch replay { case 0: return ShareWhileConnected(source: self.asObservable()) case 1: return ShareReplay1WhileConnected(source: self.asObservable()) default: return self.multicast(makeSubject: { ReplaySubject.create(bufferSize: replay) }).refCount() } } } } fileprivate final class ShareReplay1WhileConnectedConnection : ObserverType , SynchronizedUnsubscribeType { typealias E = Element typealias Observers = AnyObserver.s typealias DisposeKey = Observers.KeyType typealias Parent = ShareReplay1WhileConnected private let _parent: Parent private let _subscription = SingleAssignmentDisposable() private let _lock: RecursiveLock private var _disposed: Bool = false fileprivate var _observers = Observers() fileprivate var _element: Element? init(parent: Parent, lock: RecursiveLock) { _parent = parent _lock = lock #if TRACE_RESOURCES _ = Resources.incrementTotal() #endif } final func on(_ event: Event) { _lock.lock() let observers = _synchronized_on(event) _lock.unlock() dispatch(observers, event) } final private func _synchronized_on(_ event: Event) -> Observers { if _disposed { return Observers() } switch event { case .next(let element): _element = element return _observers case .error, .completed: let observers = _observers self._synchronized_dispose() return observers } } final func connect() { _subscription.setDisposable(_parent._source.subscribe(self)) } final func _synchronized_subscribe(_ observer: O) -> Disposable where O.E == Element { _lock.lock(); defer { _lock.unlock() } if let element = _element { observer.on(.next(element)) } let disposeKey = _observers.insert(observer.on) return SubscriptionDisposable(owner: self, key: disposeKey) } final private func _synchronized_dispose() { _disposed = true if _parent._connection === self { _parent._connection = nil } _observers = Observers() } final func synchronizedUnsubscribe(_ disposeKey: DisposeKey) { _lock.lock() let shouldDisconnect = _synchronized_unsubscribe(disposeKey) _lock.unlock() if shouldDisconnect { _subscription.dispose() } } @inline(__always) final private func _synchronized_unsubscribe(_ disposeKey: DisposeKey) -> Bool { // if already unsubscribed, just return if self._observers.removeKey(disposeKey) == nil { return false } if _observers.count == 0 { _synchronized_dispose() return true } return false } #if TRACE_RESOURCES deinit { _ = Resources.decrementTotal() } #endif } // optimized version of share replay for most common case final fileprivate class ShareReplay1WhileConnected : Observable { fileprivate typealias Connection = ShareReplay1WhileConnectedConnection fileprivate let _source: Observable fileprivate let _lock = RecursiveLock() fileprivate var _connection: Connection? init(source: Observable) { self._source = source } override func subscribe(_ observer: O) -> Disposable where O.E == E { _lock.lock() let connection = _synchronized_subscribe(observer) let count = connection._observers.count let disposable = connection._synchronized_subscribe(observer) _lock.unlock() if count == 0 { connection.connect() } return disposable } @inline(__always) private func _synchronized_subscribe(_ observer: O) -> Connection where O.E == E { let connection: Connection if let existingConnection = _connection { connection = existingConnection } else { connection = ShareReplay1WhileConnectedConnection( parent: self, lock: _lock) _connection = connection } return connection } } fileprivate final class ShareWhileConnectedConnection : ObserverType , SynchronizedUnsubscribeType { typealias E = Element typealias Observers = AnyObserver.s typealias DisposeKey = Observers.KeyType typealias Parent = ShareWhileConnected private let _parent: Parent private let _subscription = SingleAssignmentDisposable() private let _lock: RecursiveLock private var _disposed: Bool = false fileprivate var _observers = Observers() init(parent: Parent, lock: RecursiveLock) { _parent = parent _lock = lock #if TRACE_RESOURCES _ = Resources.incrementTotal() #endif } final func on(_ event: Event) { _lock.lock() let observers = _synchronized_on(event) _lock.unlock() dispatch(observers, event) } final private func _synchronized_on(_ event: Event) -> Observers { if _disposed { return Observers() } switch event { case .next: return _observers case .error, .completed: let observers = _observers self._synchronized_dispose() return observers } } final func connect() { _subscription.setDisposable(_parent._source.subscribe(self)) } final func _synchronized_subscribe(_ observer: O) -> Disposable where O.E == Element { _lock.lock(); defer { _lock.unlock() } let disposeKey = _observers.insert(observer.on) return SubscriptionDisposable(owner: self, key: disposeKey) } final private func _synchronized_dispose() { _disposed = true if _parent._connection === self { _parent._connection = nil } _observers = Observers() } final func synchronizedUnsubscribe(_ disposeKey: DisposeKey) { _lock.lock() let shouldDisconnect = _synchronized_unsubscribe(disposeKey) _lock.unlock() if shouldDisconnect { _subscription.dispose() } } @inline(__always) final private func _synchronized_unsubscribe(_ disposeKey: DisposeKey) -> Bool { // if already unsubscribed, just return if self._observers.removeKey(disposeKey) == nil { return false } if _observers.count == 0 { _synchronized_dispose() return true } return false } #if TRACE_RESOURCES deinit { _ = Resources.decrementTotal() } #endif } // optimized version of share replay for most common case final fileprivate class ShareWhileConnected : Observable { fileprivate typealias Connection = ShareWhileConnectedConnection fileprivate let _source: Observable fileprivate let _lock = RecursiveLock() fileprivate var _connection: Connection? init(source: Observable) { self._source = source } override func subscribe(_ observer: O) -> Disposable where O.E == E { _lock.lock() let connection = _synchronized_subscribe(observer) let count = connection._observers.count let disposable = connection._synchronized_subscribe(observer) _lock.unlock() if count == 0 { connection.connect() } return disposable } @inline(__always) private func _synchronized_subscribe(_ observer: O) -> Connection where O.E == E { let connection: Connection if let existingConnection = _connection { connection = existingConnection } else { connection = ShareWhileConnectedConnection( parent: self, lock: _lock) _connection = connection } return connection } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/SingleAsync.swift ================================================ // // SingleAsync.swift // RxSwift // // Created by Junior B. on 09/11/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** The single operator is similar to first, but throws a `RxError.noElements` or `RxError.moreThanOneElement` if the source Observable does not emit exactly one element before successfully completing. - seealso: [single operator on reactivex.io](http://reactivex.io/documentation/operators/first.html) - returns: An observable sequence that emits a single element or throws an exception if more (or none) of them are emitted. */ public func single() -> Observable { return SingleAsync(source: asObservable()) } /** The single operator is similar to first, but throws a `RxError.NoElements` or `RxError.MoreThanOneElement` if the source Observable does not emit exactly one element before successfully completing. - seealso: [single operator on reactivex.io](http://reactivex.io/documentation/operators/first.html) - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that emits a single element or throws an exception if more (or none) of them are emitted. */ public func single(_ predicate: @escaping (E) throws -> Bool) -> Observable { return SingleAsync(source: asObservable(), predicate: predicate) } } fileprivate final class SingleAsyncSink : Sink, ObserverType { typealias ElementType = O.E typealias Parent = SingleAsync typealias E = ElementType private let _parent: Parent private var _seenValue: Bool = false init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): do { let forward = try _parent._predicate?(value) ?? true if !forward { return } } catch let error { forwardOn(.error(error as Swift.Error)) dispose() return } if _seenValue { forwardOn(.error(RxError.moreThanOneElement)) dispose() return } _seenValue = true forwardOn(.next(value)) case .error: forwardOn(event) dispose() case .completed: if (_seenValue) { forwardOn(.completed) } else { forwardOn(.error(RxError.noElements)) } dispose() } } } final class SingleAsync: Producer { typealias Predicate = (Element) throws -> Bool fileprivate let _source: Observable fileprivate let _predicate: Predicate? init(source: Observable, predicate: Predicate? = nil) { _source = source _predicate = predicate } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = SingleAsyncSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Sink.swift ================================================ // // Sink.swift // RxSwift // // Created by Krunoslav Zaher on 2/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // class Sink : Disposable { fileprivate let _observer: O fileprivate let _cancel: Cancelable fileprivate var _disposed: Bool #if DEBUG fileprivate let _synchronizationTracker = SynchronizationTracker() #endif init(observer: O, cancel: Cancelable) { #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif _observer = observer _cancel = cancel _disposed = false } final func forwardOn(_ event: Event) { #if DEBUG _synchronizationTracker.register(synchronizationErrorMessage: .default) defer { _synchronizationTracker.unregister() } #endif if _disposed { return } _observer.on(event) } final func forwarder() -> SinkForward { return SinkForward(forward: self) } final var disposed: Bool { return _disposed } func dispose() { _disposed = true _cancel.dispose() } deinit { #if TRACE_RESOURCES let _ = Resources.decrementTotal() #endif } } final class SinkForward: ObserverType { typealias E = O.E private let _forward: Sink init(forward: Sink) { _forward = forward } final func on(_ event: Event) { switch event { case .next: _forward._observer.on(event) case .error, .completed: _forward._observer.on(event) _forward._cancel.dispose() } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Skip.swift ================================================ // // Skip.swift // RxSwift // // Created by Krunoslav Zaher on 6/25/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Bypasses a specified number of elements in an observable sequence and then returns the remaining elements. - seealso: [skip operator on reactivex.io](http://reactivex.io/documentation/operators/skip.html) - parameter count: The number of elements to skip before returning the remaining elements. - returns: An observable sequence that contains the elements that occur after the specified index in the input sequence. */ public func skip(_ count: Int) -> Observable { return SkipCount(source: asObservable(), count: count) } } extension ObservableType { /** Skips elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers. - seealso: [skip operator on reactivex.io](http://reactivex.io/documentation/operators/skip.html) - parameter duration: Duration for skipping elements from the start of the sequence. - parameter scheduler: Scheduler to run the timer on. - returns: An observable sequence with the elements skipped during the specified duration from the start of the source sequence. */ public func skip(_ duration: RxTimeInterval, scheduler: SchedulerType) -> Observable { return SkipTime(source: self.asObservable(), duration: duration, scheduler: scheduler) } } // count version final fileprivate class SkipCountSink : Sink, ObserverType { typealias Element = O.E typealias Parent = SkipCount let parent: Parent var remaining: Int init(parent: Parent, observer: O, cancel: Cancelable) { self.parent = parent self.remaining = parent.count super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): if remaining <= 0 { forwardOn(.next(value)) } else { remaining -= 1 } case .error: forwardOn(event) self.dispose() case .completed: forwardOn(event) self.dispose() } } } final fileprivate class SkipCount: Producer { let source: Observable let count: Int init(source: Observable, count: Int) { self.source = source self.count = count } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = SkipCountSink(parent: self, observer: observer, cancel: cancel) let subscription = source.subscribe(sink) return (sink: sink, subscription: subscription) } } // time version final fileprivate class SkipTimeSink : Sink, ObserverType where O.E == ElementType { typealias Parent = SkipTime typealias Element = ElementType let parent: Parent // state var open = false init(parent: Parent, observer: O, cancel: Cancelable) { self.parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): if open { forwardOn(.next(value)) } case .error: forwardOn(event) self.dispose() case .completed: forwardOn(event) self.dispose() } } func tick() { open = true } func run() -> Disposable { let disposeTimer = parent.scheduler.scheduleRelative((), dueTime: self.parent.duration) { _ in self.tick() return Disposables.create() } let disposeSubscription = parent.source.subscribe(self) return Disposables.create(disposeTimer, disposeSubscription) } } final fileprivate class SkipTime: Producer { let source: Observable let duration: RxTimeInterval let scheduler: SchedulerType init(source: Observable, duration: RxTimeInterval, scheduler: SchedulerType) { self.source = source self.scheduler = scheduler self.duration = duration } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = SkipTimeSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/SkipUntil.swift ================================================ // // SkipUntil.swift // RxSwift // // Created by Yury Korolev on 10/3/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns the elements from the source observable sequence that are emitted after the other observable sequence produces an element. - seealso: [skipUntil operator on reactivex.io](http://reactivex.io/documentation/operators/skipuntil.html) - parameter other: Observable sequence that starts propagation of elements of the source sequence. - returns: An observable sequence containing the elements of the source sequence that are emitted after the other sequence emits an item. */ public func skipUntil(_ other: O) -> Observable { return SkipUntil(source: asObservable(), other: other.asObservable()) } } final fileprivate class SkipUntilSinkOther : ObserverType , LockOwnerType , SynchronizedOnType { typealias Parent = SkipUntilSink typealias E = Other fileprivate let _parent: Parent var _lock: RecursiveLock { return _parent._lock } let _subscription = SingleAssignmentDisposable() init(parent: Parent) { _parent = parent #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next: _parent._forwardElements = true _subscription.dispose() case .error(let e): _parent.forwardOn(.error(e)) _parent.dispose() case .completed: _subscription.dispose() } } #if TRACE_RESOURCES deinit { let _ = Resources.decrementTotal() } #endif } final fileprivate class SkipUntilSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType { typealias E = O.E typealias Parent = SkipUntil let _lock = RecursiveLock() fileprivate let _parent: Parent fileprivate var _forwardElements = false fileprivate let _sourceSubscription = SingleAssignmentDisposable() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next: if _forwardElements { forwardOn(event) } case .error: forwardOn(event) self.dispose() case .completed: if _forwardElements { forwardOn(event) } self.dispose() } } func run() -> Disposable { let sourceSubscription = _parent._source.subscribe(self) let otherObserver = SkipUntilSinkOther(parent: self) let otherSubscription = _parent._other.subscribe(otherObserver) _sourceSubscription.setDisposable(sourceSubscription) otherObserver._subscription.setDisposable(otherSubscription) return Disposables.create(_sourceSubscription, otherObserver._subscription) } } final fileprivate class SkipUntil: Producer { fileprivate let _source: Observable fileprivate let _other: Observable init(source: Observable, other: Observable) { _source = source _other = other } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = SkipUntilSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/SkipWhile.swift ================================================ // // SkipWhile.swift // RxSwift // // Created by Yury Korolev on 10/9/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. - seealso: [skipWhile operator on reactivex.io](http://reactivex.io/documentation/operators/skipwhile.html) - parameter predicate: A function to test each element for a condition. - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. */ public func skipWhile(_ predicate: @escaping (E) throws -> Bool) -> Observable { return SkipWhile(source: asObservable(), predicate: predicate) } } final fileprivate class SkipWhileSink : Sink, ObserverType { typealias Element = O.E typealias Parent = SkipWhile fileprivate let _parent: Parent fileprivate var _running = false init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): if !_running { do { _running = try !_parent._predicate(value) } catch let e { forwardOn(.error(e)) dispose() return } } if _running { forwardOn(.next(value)) } case .error, .completed: forwardOn(event) dispose() } } } final fileprivate class SkipWhile: Producer { typealias Predicate = (Element) throws -> Bool typealias PredicateWithIndex = (Element, Int) throws -> Bool fileprivate let _source: Observable fileprivate let _predicate: Predicate init(source: Observable, predicate: @escaping Predicate) { _source = source _predicate = predicate } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = SkipWhileSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/StartWith.swift ================================================ // // StartWith.swift // RxSwift // // Created by Krunoslav Zaher on 4/6/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Prepends a sequence of values to an observable sequence. - seealso: [startWith operator on reactivex.io](http://reactivex.io/documentation/operators/startwith.html) - parameter elements: Elements to prepend to the specified sequence. - returns: The source sequence prepended with the specified values. */ public func startWith(_ elements: E ...) -> Observable { return StartWith(source: self.asObservable(), elements: elements) } } final fileprivate class StartWith: Producer { let elements: [Element] let source: Observable init(source: Observable, elements: [Element]) { self.source = source self.elements = elements super.init() } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { for e in elements { observer.on(.next(e)) } return (sink: Disposables.create(), subscription: source.subscribe(observer)) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/SubscribeOn.swift ================================================ // // SubscribeOn.swift // RxSwift // // Created by Krunoslav Zaher on 6/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Wraps the source sequence in order to run its subscription and unsubscription logic on the specified scheduler. This operation is not commonly used. This only performs the side-effects of subscription and unsubscription on the specified scheduler. In order to invoke observer callbacks on a `scheduler`, use `observeOn`. - seealso: [subscribeOn operator on reactivex.io](http://reactivex.io/documentation/operators/subscribeon.html) - parameter scheduler: Scheduler to perform subscription and unsubscription actions on. - returns: The source sequence whose subscriptions and unsubscriptions happen on the specified scheduler. */ public func subscribeOn(_ scheduler: ImmediateSchedulerType) -> Observable { return SubscribeOn(source: self, scheduler: scheduler) } } final fileprivate class SubscribeOnSink : Sink, ObserverType where Ob.E == O.E { typealias Element = O.E typealias Parent = SubscribeOn let parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { self.parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { forwardOn(event) if event.isStopEvent { self.dispose() } } func run() -> Disposable { let disposeEverything = SerialDisposable() let cancelSchedule = SingleAssignmentDisposable() disposeEverything.disposable = cancelSchedule let disposeSchedule = parent.scheduler.schedule(()) { (_) -> Disposable in let subscription = self.parent.source.subscribe(self) disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription) return Disposables.create() } cancelSchedule.setDisposable(disposeSchedule) return disposeEverything } } final fileprivate class SubscribeOn : Producer { let source: Ob let scheduler: ImmediateSchedulerType init(source: Ob, scheduler: ImmediateSchedulerType) { self.source = source self.scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Ob.E { let sink = SubscribeOnSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Switch.swift ================================================ // // Switch.swift // RxSwift // // Created by Krunoslav Zaher on 3/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Projects each element of an observable sequence into a new sequence of observable sequences and then transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. It is a combination of `map` + `switchLatest` operator - seealso: [flatMapLatest operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html) - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ public func flatMapLatest(_ selector: @escaping (E) throws -> O) -> Observable { return FlatMapLatest(source: asObservable(), selector: selector) } } extension ObservableType where E : ObservableConvertibleType { /** Transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. Each time a new inner observable sequence is received, unsubscribe from the previous inner observable sequence. - seealso: [switch operator on reactivex.io](http://reactivex.io/documentation/operators/switch.html) - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ public func switchLatest() -> Observable { return Switch(source: asObservable()) } } fileprivate class SwitchSink : Sink , ObserverType where S.E == O.E { typealias E = SourceType fileprivate let _subscriptions: SingleAssignmentDisposable = SingleAssignmentDisposable() fileprivate let _innerSubscription: SerialDisposable = SerialDisposable() let _lock = RecursiveLock() // state fileprivate var _stopped = false fileprivate var _latest = 0 fileprivate var _hasLatest = false override init(observer: O, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } func run(_ source: Observable) -> Disposable { let subscription = source.subscribe(self) _subscriptions.setDisposable(subscription) return Disposables.create(_subscriptions, _innerSubscription) } func performMap(_ element: SourceType) throws -> S { rxAbstractMethod() } @inline(__always) final private func nextElementArrived(element: E) -> (Int, Observable)? { _lock.lock(); defer { _lock.unlock() } // { do { let observable = try performMap(element).asObservable() _hasLatest = true _latest = _latest &+ 1 return (_latest, observable) } catch let error { forwardOn(.error(error)) dispose() } return nil // } } func on(_ event: Event) { switch event { case .next(let element): if let (latest, observable) = nextElementArrived(element: element) { let d = SingleAssignmentDisposable() _innerSubscription.disposable = d let observer = SwitchSinkIter(parent: self, id: latest, _self: d) let disposable = observable.subscribe(observer) d.setDisposable(disposable) } case .error(let error): _lock.lock(); defer { _lock.unlock() } forwardOn(.error(error)) dispose() case .completed: _lock.lock(); defer { _lock.unlock() } _stopped = true _subscriptions.dispose() if !_hasLatest { forwardOn(.completed) dispose() } } } } final fileprivate class SwitchSinkIter : ObserverType , LockOwnerType , SynchronizedOnType where S.E == O.E { typealias E = S.E typealias Parent = SwitchSink fileprivate let _parent: Parent fileprivate let _id: Int fileprivate let _self: Disposable var _lock: RecursiveLock { return _parent._lock } init(parent: Parent, id: Int, _self: Disposable) { _parent = parent _id = id self._self = _self } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next: break case .error, .completed: _self.dispose() } if _parent._latest != _id { return } switch event { case .next: _parent.forwardOn(event) case .error: _parent.forwardOn(event) _parent.dispose() case .completed: _parent._hasLatest = false if _parent._stopped { _parent.forwardOn(event) _parent.dispose() } } } } // MARK: Specializations final fileprivate class SwitchIdentitySink : SwitchSink where O.E == S.E { override init(observer: O, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } override func performMap(_ element: S) throws -> S { return element } } final fileprivate class MapSwitchSink : SwitchSink where O.E == S.E { typealias Selector = (SourceType) throws -> S fileprivate let _selector: Selector init(selector: @escaping Selector, observer: O, cancel: Cancelable) { _selector = selector super.init(observer: observer, cancel: cancel) } override func performMap(_ element: SourceType) throws -> S { return try _selector(element) } } // MARK: Producers final fileprivate class Switch : Producer { fileprivate let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == S.E { let sink = SwitchIdentitySink(observer: observer, cancel: cancel) let subscription = sink.run(_source) return (sink: sink, subscription: subscription) } } final fileprivate class FlatMapLatest : Producer { typealias Selector = (SourceType) throws -> S fileprivate let _source: Observable fileprivate let _selector: Selector init(source: Observable, selector: @escaping Selector) { _source = source _selector = selector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == S.E { let sink = MapSwitchSink(selector: _selector, observer: observer, cancel: cancel) let subscription = sink.run(_source) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/SwitchIfEmpty.swift ================================================ // // SwitchIfEmpty.swift // RxSwift // // Created by sergdort on 23/12/2016. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns the elements of the specified sequence or `switchTo` sequence if the sequence is empty. - seealso: [DefaultIfEmpty operator on reactivex.io](http://reactivex.io/documentation/operators/defaultifempty.html) - parameter switchTo: Observable sequence being returned when source sequence is empty. - returns: Observable sequence that contains elements from switchTo sequence if source is empty, otherwise returns source sequence elements. */ public func ifEmpty(switchTo other: Observable) -> Observable { return SwitchIfEmpty(source: asObservable(), ifEmpty: other) } } final fileprivate class SwitchIfEmpty: Producer { private let _source: Observable private let _ifEmpty: Observable init(source: Observable, ifEmpty: Observable) { _source = source _ifEmpty = ifEmpty } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = SwitchIfEmptySink(ifEmpty: _ifEmpty, observer: observer, cancel: cancel) let subscription = sink.run(_source.asObservable()) return (sink: sink, subscription: subscription) } } final fileprivate class SwitchIfEmptySink: Sink , ObserverType { typealias E = O.E private let _ifEmpty: Observable private var _isEmpty = true private let _ifEmptySubscription = SingleAssignmentDisposable() init(ifEmpty: Observable, observer: O, cancel: Cancelable) { _ifEmpty = ifEmpty super.init(observer: observer, cancel: cancel) } func run(_ source: Observable) -> Disposable { let subscription = source.subscribe(self) return Disposables.create(subscription, _ifEmptySubscription) } func on(_ event: Event) { switch event { case .next: _isEmpty = false forwardOn(event) case .error: forwardOn(event) dispose() case .completed: guard _isEmpty else { forwardOn(.completed) dispose() return } let ifEmptySink = SwitchIfEmptySinkIter(parent: self) _ifEmptySubscription.setDisposable(_ifEmpty.subscribe(ifEmptySink)) } } } final fileprivate class SwitchIfEmptySinkIter : ObserverType { typealias E = O.E typealias Parent = SwitchIfEmptySink private let _parent: Parent init(parent: Parent) { _parent = parent } func on(_ event: Event) { switch event { case .next: _parent.forwardOn(event) case .error: _parent.forwardOn(event) _parent.dispose() case .completed: _parent.forwardOn(event) _parent.dispose() } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Take.swift ================================================ // // Take.swift // RxSwift // // Created by Krunoslav Zaher on 6/12/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns a specified number of contiguous elements from the start of an observable sequence. - seealso: [take operator on reactivex.io](http://reactivex.io/documentation/operators/take.html) - parameter count: The number of elements to return. - returns: An observable sequence that contains the specified number of elements from the start of the input sequence. */ public func take(_ count: Int) -> Observable { if count == 0 { return Observable.empty() } else { return TakeCount(source: asObservable(), count: count) } } } extension ObservableType { /** Takes elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers. - seealso: [take operator on reactivex.io](http://reactivex.io/documentation/operators/take.html) - parameter duration: Duration for taking elements from the start of the sequence. - parameter scheduler: Scheduler to run the timer on. - returns: An observable sequence with the elements taken during the specified duration from the start of the source sequence. */ public func take(_ duration: RxTimeInterval, scheduler: SchedulerType) -> Observable { return TakeTime(source: self.asObservable(), duration: duration, scheduler: scheduler) } } // count version final fileprivate class TakeCountSink : Sink, ObserverType { typealias E = O.E typealias Parent = TakeCount private let _parent: Parent private var _remaining: Int init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _remaining = parent._count super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): if _remaining > 0 { _remaining -= 1 forwardOn(.next(value)) if _remaining == 0 { forwardOn(.completed) dispose() } } case .error: forwardOn(event) dispose() case .completed: forwardOn(event) dispose() } } } final fileprivate class TakeCount: Producer { fileprivate let _source: Observable fileprivate let _count: Int init(source: Observable, count: Int) { if count < 0 { rxFatalError("count can't be negative") } _source = source _count = count } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = TakeCountSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } // time version final fileprivate class TakeTimeSink : Sink , LockOwnerType , ObserverType , SynchronizedOnType where O.E == ElementType { typealias Parent = TakeTime typealias E = ElementType fileprivate let _parent: Parent let _lock = RecursiveLock() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next(let value): forwardOn(.next(value)) case .error: forwardOn(event) dispose() case .completed: forwardOn(event) dispose() } } func tick() { _lock.lock(); defer { _lock.unlock() } forwardOn(.completed) dispose() } func run() -> Disposable { let disposeTimer = _parent._scheduler.scheduleRelative((), dueTime: _parent._duration) { _ in self.tick() return Disposables.create() } let disposeSubscription = _parent._source.subscribe(self) return Disposables.create(disposeTimer, disposeSubscription) } } final fileprivate class TakeTime : Producer { typealias TimeInterval = RxTimeInterval fileprivate let _source: Observable fileprivate let _duration: TimeInterval fileprivate let _scheduler: SchedulerType init(source: Observable, duration: TimeInterval, scheduler: SchedulerType) { _source = source _scheduler = scheduler _duration = duration } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = TakeTimeSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/TakeLast.swift ================================================ // // TakeLast.swift // RxSwift // // Created by Tomi Koskinen on 25/10/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns a specified number of contiguous elements from the end of an observable sequence. This operator accumulates a buffer with a length enough to store elements count elements. Upon completion of the source sequence, this buffer is drained on the result sequence. This causes the elements to be delayed. - seealso: [takeLast operator on reactivex.io](http://reactivex.io/documentation/operators/takelast.html) - parameter count: Number of elements to take from the end of the source sequence. - returns: An observable sequence containing the specified number of elements from the end of the source sequence. */ public func takeLast(_ count: Int) -> Observable { return TakeLast(source: asObservable(), count: count) } } final fileprivate class TakeLastSink : Sink, ObserverType { typealias E = O.E typealias Parent = TakeLast private let _parent: Parent private var _elements: Queue init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _elements = Queue(capacity: parent._count + 1) super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): _elements.enqueue(value) if _elements.count > self._parent._count { let _ = _elements.dequeue() } case .error: forwardOn(event) dispose() case .completed: for e in _elements { forwardOn(.next(e)) } forwardOn(.completed) dispose() } } } final fileprivate class TakeLast: Producer { fileprivate let _source: Observable fileprivate let _count: Int init(source: Observable, count: Int) { if count < 0 { rxFatalError("count can't be negative") } _source = source _count = count } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = TakeLastSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/TakeUntil.swift ================================================ // // TakeUntil.swift // RxSwift // // Created by Krunoslav Zaher on 6/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns the elements from the source observable sequence until the other observable sequence produces an element. - seealso: [takeUntil operator on reactivex.io](http://reactivex.io/documentation/operators/takeuntil.html) - parameter other: Observable sequence that terminates propagation of elements of the source sequence. - returns: An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation. */ public func takeUntil(_ other: O) -> Observable { return TakeUntil(source: asObservable(), other: other.asObservable()) } } final fileprivate class TakeUntilSinkOther : ObserverType , LockOwnerType , SynchronizedOnType { typealias Parent = TakeUntilSink typealias E = Other fileprivate let _parent: Parent var _lock: RecursiveLock { return _parent._lock } fileprivate let _subscription = SingleAssignmentDisposable() init(parent: Parent) { _parent = parent #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next: _parent.forwardOn(.completed) _parent.dispose() case .error(let e): _parent.forwardOn(.error(e)) _parent.dispose() case .completed: _subscription.dispose() } } #if TRACE_RESOURCES deinit { let _ = Resources.decrementTotal() } #endif } final fileprivate class TakeUntilSink : Sink , LockOwnerType , ObserverType , SynchronizedOnType { typealias E = O.E typealias Parent = TakeUntil fileprivate let _parent: Parent let _lock = RecursiveLock() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next: forwardOn(event) case .error: forwardOn(event) dispose() case .completed: forwardOn(event) dispose() } } func run() -> Disposable { let otherObserver = TakeUntilSinkOther(parent: self) let otherSubscription = _parent._other.subscribe(otherObserver) otherObserver._subscription.setDisposable(otherSubscription) let sourceSubscription = _parent._source.subscribe(self) return Disposables.create(sourceSubscription, otherObserver._subscription) } } final fileprivate class TakeUntil: Producer { fileprivate let _source: Observable fileprivate let _other: Observable init(source: Observable, other: Observable) { _source = source _other = other } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = TakeUntilSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/TakeWhile.swift ================================================ // // TakeWhile.swift // RxSwift // // Created by Krunoslav Zaher on 6/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Returns elements from an observable sequence as long as a specified condition is true. - seealso: [takeWhile operator on reactivex.io](http://reactivex.io/documentation/operators/takewhile.html) - parameter predicate: A function to test each element for a condition. - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ public func takeWhile(_ predicate: @escaping (E) throws -> Bool) -> Observable { return TakeWhile(source: asObservable(), predicate: predicate) } } final fileprivate class TakeWhileSink : Sink , ObserverType { typealias Element = O.E typealias Parent = TakeWhile fileprivate let _parent: Parent fileprivate var _running = true init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): if !_running { return } do { _running = try _parent._predicate(value) } catch let e { forwardOn(.error(e)) dispose() return } if _running { forwardOn(.next(value)) } else { forwardOn(.completed) dispose() } case .error, .completed: forwardOn(event) dispose() } } } final fileprivate class TakeWhile: Producer { typealias Predicate = (Element) throws -> Bool fileprivate let _source: Observable fileprivate let _predicate: Predicate init(source: Observable, predicate: @escaping Predicate) { _source = source _predicate = predicate } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = TakeWhileSink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Throttle.swift ================================================ // // Throttle.swift // RxSwift // // Created by Krunoslav Zaher on 3/22/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.Date extension ObservableType { /** Returns an Observable that emits the first and the latest item emitted by the source Observable during sequential time windows of a specified duration. This operator makes sure that no two elements are emitted in less then dueTime. - seealso: [debounce operator on reactivex.io](http://reactivex.io/documentation/operators/debounce.html) - parameter dueTime: Throttling duration for each element. - parameter latest: Should latest element received in a dueTime wide time window since last element emission be emitted. - parameter scheduler: Scheduler to run the throttle timers on. - returns: The throttled sequence. */ public func throttle(_ dueTime: RxTimeInterval, latest: Bool = true, scheduler: SchedulerType) -> Observable { return Throttle(source: self.asObservable(), dueTime: dueTime, latest: latest, scheduler: scheduler) } } final fileprivate class ThrottleSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType { typealias Element = O.E typealias ParentType = Throttle private let _parent: ParentType let _lock = RecursiveLock() // state private var _lastUnsentElement: Element? = nil private var _lastSentTime: Date? = nil private var _completed: Bool = false let cancellable = SerialDisposable() init(parent: ParentType, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let subscription = _parent._source.subscribe(self) return Disposables.create(subscription, cancellable) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case .next(let element): let now = _parent._scheduler.now let timeIntervalSinceLast: RxTimeInterval if let lastSendingTime = _lastSentTime { timeIntervalSinceLast = now.timeIntervalSince(lastSendingTime) } else { timeIntervalSinceLast = _parent._dueTime } let couldSendNow = timeIntervalSinceLast >= _parent._dueTime if couldSendNow { self.sendNow(element: element) return } if !_parent._latest { return } let isThereAlreadyInFlightRequest = _lastUnsentElement != nil _lastUnsentElement = element if isThereAlreadyInFlightRequest { return } let scheduler = _parent._scheduler let dueTime = _parent._dueTime let d = SingleAssignmentDisposable() self.cancellable.disposable = d d.setDisposable(scheduler.scheduleRelative(0, dueTime: dueTime - timeIntervalSinceLast, action: self.propagate)) case .error: _lastUnsentElement = nil forwardOn(event) dispose() case .completed: if let _ = _lastUnsentElement { _completed = true } else { forwardOn(.completed) dispose() } } } private func sendNow(element: Element) { _lastUnsentElement = nil self.forwardOn(.next(element)) // in case element processing takes a while, this should give some more room _lastSentTime = _parent._scheduler.now } func propagate(_: Int) -> Disposable { _lock.lock(); defer { _lock.unlock() } // { if let lastUnsentElement = _lastUnsentElement { sendNow(element: lastUnsentElement) } if _completed { forwardOn(.completed) dispose() } // } return Disposables.create() } } final fileprivate class Throttle : Producer { fileprivate let _source: Observable fileprivate let _dueTime: RxTimeInterval fileprivate let _latest: Bool fileprivate let _scheduler: SchedulerType init(source: Observable, dueTime: RxTimeInterval, latest: Bool, scheduler: SchedulerType) { _source = source _dueTime = dueTime _latest = latest _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = ThrottleSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Timeout.swift ================================================ // // Timeout.swift // RxSwift // // Created by Tomi Koskinen on 13/11/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Applies a timeout policy for each element in the observable sequence. If the next element isn't received within the specified timeout duration starting from its predecessor, a TimeoutError is propagated to the observer. - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html) - parameter dueTime: Maximum duration between values before a timeout occurs. - parameter scheduler: Scheduler to run the timeout timer on. - returns: An observable sequence with a `RxError.timeout` in case of a timeout. */ public func timeout(_ dueTime: RxTimeInterval, scheduler: SchedulerType) -> Observable { return Timeout(source: self.asObservable(), dueTime: dueTime, other: Observable.error(RxError.timeout), scheduler: scheduler) } /** Applies a timeout policy for each element in the observable sequence, using the specified scheduler to run timeout timers. If the next element isn't received within the specified timeout duration starting from its predecessor, the other observable sequence is used to produce future messages from that point on. - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html) - parameter dueTime: Maximum duration between values before a timeout occurs. - parameter other: Sequence to return in case of a timeout. - parameter scheduler: Scheduler to run the timeout timer on. - returns: The source sequence switching to the other sequence in case of a timeout. */ public func timeout(_ dueTime: RxTimeInterval, other: O, scheduler: SchedulerType) -> Observable where E == O.E { return Timeout(source: self.asObservable(), dueTime: dueTime, other: other.asObservable(), scheduler: scheduler) } } final fileprivate class TimeoutSink: Sink, LockOwnerType, ObserverType { typealias E = O.E typealias Parent = Timeout private let _parent: Parent let _lock = RecursiveLock() private let _timerD = SerialDisposable() private let _subscription = SerialDisposable() private var _id = 0 private var _switched = false init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let original = SingleAssignmentDisposable() _subscription.disposable = original _createTimeoutTimer() original.setDisposable(_parent._source.subscribe(self)) return Disposables.create(_subscription, _timerD) } func on(_ event: Event) { switch event { case .next: var onNextWins = false _lock.performLocked() { onNextWins = !self._switched if onNextWins { self._id = self._id &+ 1 } } if onNextWins { forwardOn(event) self._createTimeoutTimer() } case .error, .completed: var onEventWins = false _lock.performLocked() { onEventWins = !self._switched if onEventWins { self._id = self._id &+ 1 } } if onEventWins { forwardOn(event) self.dispose() } } } private func _createTimeoutTimer() { if _timerD.isDisposed { return } let nextTimer = SingleAssignmentDisposable() _timerD.disposable = nextTimer let disposeSchedule = _parent._scheduler.scheduleRelative(_id, dueTime: _parent._dueTime) { state in var timerWins = false self._lock.performLocked() { self._switched = (state == self._id) timerWins = self._switched } if timerWins { self._subscription.disposable = self._parent._other.subscribe(self.forwarder()) } return Disposables.create() } nextTimer.setDisposable(disposeSchedule) } } final fileprivate class Timeout : Producer { fileprivate let _source: Observable fileprivate let _dueTime: RxTimeInterval fileprivate let _other: Observable fileprivate let _scheduler: SchedulerType init(source: Observable, dueTime: RxTimeInterval, other: Observable, scheduler: SchedulerType) { _source = source _dueTime = dueTime _other = other _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element { let sink = TimeoutSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Timer.swift ================================================ // // Timer.swift // RxSwift // // Created by Krunoslav Zaher on 6/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType where E : RxAbstractInteger { /** Returns an observable sequence that produces a value after each period, using the specified scheduler to run timers and to send out observer messages. - seealso: [interval operator on reactivex.io](http://reactivex.io/documentation/operators/interval.html) - parameter period: Period for producing the values in the resulting sequence. - parameter scheduler: Scheduler to run the timer on. - returns: An observable sequence that produces a value after each period. */ public static func interval(_ period: RxTimeInterval, scheduler: SchedulerType) -> Observable { return Timer(dueTime: period, period: period, scheduler: scheduler ) } } extension ObservableType where E: RxAbstractInteger { /** Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers. - seealso: [timer operator on reactivex.io](http://reactivex.io/documentation/operators/timer.html) - parameter dueTime: Relative time at which to produce the first value. - parameter period: Period to produce subsequent values. - parameter scheduler: Scheduler to run timers on. - returns: An observable sequence that produces a value after due time has elapsed and then each period. */ public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType) -> Observable { return Timer( dueTime: dueTime, period: period, scheduler: scheduler ) } } final fileprivate class TimerSink : Sink where O.E : RxAbstractInteger { typealias Parent = Timer private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { return _parent._scheduler.schedulePeriodic(0 as O.E, startAfter: _parent._dueTime, period: _parent._period!) { state in self.forwardOn(.next(state)) return state &+ 1 } } } final fileprivate class TimerOneOffSink : Sink where O.E : RxAbstractInteger { typealias Parent = Timer private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { return _parent._scheduler.scheduleRelative(self, dueTime: _parent._dueTime) { (`self`) -> Disposable in self.forwardOn(.next(0)) self.forwardOn(.completed) self.dispose() return Disposables.create() } } } final fileprivate class Timer: Producer { fileprivate let _scheduler: SchedulerType fileprivate let _dueTime: RxTimeInterval fileprivate let _period: RxTimeInterval? init(dueTime: RxTimeInterval, period: RxTimeInterval?, scheduler: SchedulerType) { _scheduler = scheduler _dueTime = dueTime _period = period } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { if let _ = _period { let sink = TimerSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } else { let sink = TimerOneOffSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/ToArray.swift ================================================ // // ToArray.swift // RxSwift // // Created by Junior B. on 20/10/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Converts an Observable into another Observable that emits the whole sequence as a single array and then terminates. For aggregation behavior see `reduce`. - seealso: [toArray operator on reactivex.io](http://reactivex.io/documentation/operators/to.html) - returns: An observable sequence containing all the emitted elements as array. */ public func toArray() -> Observable<[E]> { return ToArray(source: self.asObservable()) } } final fileprivate class ToArraySink : Sink, ObserverType where O.E == [SourceType] { typealias Parent = ToArray let _parent: Parent var _list = Array() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func on(_ event: Event) { switch event { case .next(let value): self._list.append(value) case .error(let e): forwardOn(.error(e)) self.dispose() case .completed: forwardOn(.next(_list)) forwardOn(.completed) self.dispose() } } } final fileprivate class ToArray : Producer<[SourceType]> { let _source: Observable init(source: Observable) { _source = source } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == [SourceType] { let sink = ToArraySink(parent: self, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Using.swift ================================================ // // Using.swift // RxSwift // // Created by Yury Korolev on 10/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Constructs an observable sequence that depends on a resource object, whose lifetime is tied to the resulting observable sequence's lifetime. - seealso: [using operator on reactivex.io](http://reactivex.io/documentation/operators/using.html) - parameter resourceFactory: Factory function to obtain a resource object. - parameter observableFactory: Factory function to obtain an observable sequence that depends on the obtained resource. - returns: An observable sequence whose lifetime controls the lifetime of the dependent resource object. */ public static func using(_ resourceFactory: @escaping () throws -> Resource, observableFactory: @escaping (Resource) throws -> Observable) -> Observable { return Using(resourceFactory: resourceFactory, observableFactory: observableFactory) } } final fileprivate class UsingSink : Sink, ObserverType { typealias SourceType = O.E typealias Parent = Using private let _parent: Parent init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { var disposable = Disposables.create() do { let resource = try _parent._resourceFactory() disposable = resource let source = try _parent._observableFactory(resource) return Disposables.create( source.subscribe(self), disposable ) } catch let error { return Disposables.create( Observable.error(error).subscribe(self), disposable ) } } func on(_ event: Event) { switch event { case let .next(value): forwardOn(.next(value)) case let .error(error): forwardOn(.error(error)) dispose() case .completed: forwardOn(.completed) dispose() } } } final fileprivate class Using: Producer { typealias E = SourceType typealias ResourceFactory = () throws -> ResourceType typealias ObservableFactory = (ResourceType) throws -> Observable fileprivate let _resourceFactory: ResourceFactory fileprivate let _observableFactory: ObservableFactory init(resourceFactory: @escaping ResourceFactory, observableFactory: @escaping ObservableFactory) { _resourceFactory = resourceFactory _observableFactory = observableFactory } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E { let sink = UsingSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Window.swift ================================================ // // Window.swift // RxSwift // // Created by Junior B. on 29/10/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Projects each element of an observable sequence into a window that is completed when either it’s full or a given amount of time has elapsed. - seealso: [window operator on reactivex.io](http://reactivex.io/documentation/operators/window.html) - parameter timeSpan: Maximum time length of a window. - parameter count: Maximum element count of a window. - parameter scheduler: Scheduler to run windowing timers on. - returns: An observable sequence of windows (instances of `Observable`). */ public func window(timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType) -> Observable> { return WindowTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler) } } final fileprivate class WindowTimeCountSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType where O.E == Observable { typealias Parent = WindowTimeCount typealias E = Element private let _parent: Parent let _lock = RecursiveLock() private var _subject = PublishSubject() private var _count = 0 private var _windowId = 0 private let _timerD = SerialDisposable() private let _refCountDisposable: RefCountDisposable private let _groupDisposable = CompositeDisposable() init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent let _ = _groupDisposable.insert(_timerD) _refCountDisposable = RefCountDisposable(disposable: _groupDisposable) super.init(observer: observer, cancel: cancel) } func run() -> Disposable { forwardOn(.next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) createTimer(_windowId) let _ = _groupDisposable.insert(_parent._source.subscribe(self)) return _refCountDisposable } func startNewWindowAndCompleteCurrentOne() { _subject.on(.completed) _subject = PublishSubject() forwardOn(.next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { var newWindow = false var newId = 0 switch event { case .next(let element): _subject.on(.next(element)) do { let _ = try incrementChecked(&_count) } catch (let e) { _subject.on(.error(e as Swift.Error)) dispose() } if (_count == _parent._count) { newWindow = true _count = 0 _windowId += 1 newId = _windowId self.startNewWindowAndCompleteCurrentOne() } case .error(let error): _subject.on(.error(error)) forwardOn(.error(error)) dispose() case .completed: _subject.on(.completed) forwardOn(.completed) dispose() } if newWindow { createTimer(newId) } } func createTimer(_ windowId: Int) { if _timerD.isDisposed { return } if _windowId != windowId { return } let nextTimer = SingleAssignmentDisposable() _timerD.disposable = nextTimer let scheduledRelative = _parent._scheduler.scheduleRelative(windowId, dueTime: _parent._timeSpan) { previousWindowId in var newId = 0 self._lock.performLocked { if previousWindowId != self._windowId { return } self._count = 0 self._windowId = self._windowId &+ 1 newId = self._windowId self.startNewWindowAndCompleteCurrentOne() } self.createTimer(newId) return Disposables.create() } nextTimer.setDisposable(scheduledRelative) } } final fileprivate class WindowTimeCount : Producer> { fileprivate let _timeSpan: RxTimeInterval fileprivate let _count: Int fileprivate let _scheduler: SchedulerType fileprivate let _source: Observable init(source: Observable, timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType) { _source = source _timeSpan = timeSpan _count = count _scheduler = scheduler } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Observable { let sink = WindowTimeCountSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/WithLatestFrom.swift ================================================ // // WithLatestFrom.swift // RxSwift // // Created by Yury Korolev on 10/19/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter second: Second observable source. - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any. - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(_ second: SecondO, resultSelector: @escaping (E, SecondO.E) throws -> ResultType) -> Observable { return WithLatestFrom(first: asObservable(), second: second.asObservable(), resultSelector: resultSelector) } /** Merges two observable sequences into one observable sequence by using latest element from the second sequence every time when `self` emits an element. - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html) - parameter second: Second observable source. - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(_ second: SecondO) -> Observable { return WithLatestFrom(first: asObservable(), second: second.asObservable(), resultSelector: { $1 }) } } final fileprivate class WithLatestFromSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType { typealias ResultType = O.E typealias Parent = WithLatestFrom typealias E = FirstType fileprivate let _parent: Parent var _lock = RecursiveLock() fileprivate var _latest: SecondType? init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let sndSubscription = SingleAssignmentDisposable() let sndO = WithLatestFromSecond(parent: self, disposable: sndSubscription) sndSubscription.setDisposable(_parent._second.subscribe(sndO)) let fstSubscription = _parent._first.subscribe(self) return Disposables.create(fstSubscription, sndSubscription) } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case let .next(value): guard let latest = _latest else { return } do { let res = try _parent._resultSelector(value, latest) forwardOn(.next(res)) } catch let e { forwardOn(.error(e)) dispose() } case .completed: forwardOn(.completed) dispose() case let .error(error): forwardOn(.error(error)) dispose() } } } final fileprivate class WithLatestFromSecond : ObserverType , LockOwnerType , SynchronizedOnType { typealias ResultType = O.E typealias Parent = WithLatestFromSink typealias E = SecondType private let _parent: Parent private let _disposable: Disposable var _lock: RecursiveLock { return _parent._lock } init(parent: Parent, disposable: Disposable) { _parent = parent _disposable = disposable } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { switch event { case let .next(value): _parent._latest = value case .completed: _disposable.dispose() case let .error(error): _parent.forwardOn(.error(error)) _parent.dispose() } } } final fileprivate class WithLatestFrom: Producer { typealias ResultSelector = (FirstType, SecondType) throws -> ResultType fileprivate let _first: Observable fileprivate let _second: Observable fileprivate let _resultSelector: ResultSelector init(first: Observable, second: Observable, resultSelector: @escaping ResultSelector) { _first = first _second = second _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == ResultType { let sink = WithLatestFromSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Zip+Collection.swift ================================================ // // Zip+Collection.swift // RxSwift // // Created by Krunoslav Zaher on 8/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip(_ collection: C, _ resultSelector: @escaping ([C.Iterator.Element.E]) throws -> E) -> Observable where C.Iterator.Element: ObservableType { return ZipCollectionType(sources: collection, resultSelector: resultSelector) } /** Merges the specified observable sequences into one observable sequence whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip(_ collection: C) -> Observable<[E]> where C.Iterator.Element: ObservableType, C.Iterator.Element.E == E { return ZipCollectionType(sources: collection, resultSelector: { $0 }) } } final fileprivate class ZipCollectionTypeSink : Sink where C.Iterator.Element : ObservableConvertibleType { typealias R = O.E typealias Parent = ZipCollectionType typealias SourceElement = C.Iterator.Element.E private let _parent: Parent private let _lock = RecursiveLock() // state private var _numberOfValues = 0 private var _values: [Queue] private var _isDone: [Bool] private var _numberOfDone = 0 private var _subscriptions: [SingleAssignmentDisposable] init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent _values = [Queue](repeating: Queue(capacity: 4), count: parent.count) _isDone = [Bool](repeating: false, count: parent.count) _subscriptions = Array() _subscriptions.reserveCapacity(parent.count) for _ in 0 ..< parent.count { _subscriptions.append(SingleAssignmentDisposable()) } super.init(observer: observer, cancel: cancel) } func on(_ event: Event, atIndex: Int) { _lock.lock(); defer { _lock.unlock() } // { switch event { case .next(let element): _values[atIndex].enqueue(element) if _values[atIndex].count == 1 { _numberOfValues += 1 } if _numberOfValues < _parent.count { if _numberOfDone == _parent.count - 1 { self.forwardOn(.completed) self.dispose() } return } do { var arguments = [SourceElement]() arguments.reserveCapacity(_parent.count) // recalculate number of values _numberOfValues = 0 for i in 0 ..< _values.count { arguments.append(_values[i].dequeue()!) if _values[i].count > 0 { _numberOfValues += 1 } } let result = try _parent.resultSelector(arguments) self.forwardOn(.next(result)) } catch let error { self.forwardOn(.error(error)) self.dispose() } case .error(let error): self.forwardOn(.error(error)) self.dispose() case .completed: if _isDone[atIndex] { return } _isDone[atIndex] = true _numberOfDone += 1 if _numberOfDone == _parent.count { self.forwardOn(.completed) self.dispose() } else { _subscriptions[atIndex].dispose() } } // } } func run() -> Disposable { var j = 0 for i in _parent.sources { let index = j let source = i.asObservable() let disposable = source.subscribe(AnyObserver { event in self.on(event, atIndex: index) }) _subscriptions[j].setDisposable(disposable) j += 1 } if _parent.sources.isEmpty { self.forwardOn(.completed) } return Disposables.create(_subscriptions) } } final fileprivate class ZipCollectionType : Producer where C.Iterator.Element : ObservableConvertibleType { typealias ResultSelector = ([C.Iterator.Element.E]) throws -> R let sources: C let resultSelector: ResultSelector let count: Int init(sources: C, resultSelector: @escaping ResultSelector) { self.sources = sources self.resultSelector = resultSelector self.count = Int(Int64(self.sources.count)) } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipCollectionTypeSink(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Zip+arity.swift ================================================ // This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project // // Zip+arity.swift // RxSwift // // Created by Krunoslav Zaher on 5/23/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // // 2 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.E, O2.E) throws -> E) -> Observable { return Zip2( source1: source1.asObservable(), source2: source2.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2) -> Observable<(O1.E, O2.E)> { return Zip2( source1: source1.asObservable(), source2: source2.asObservable(), resultSelector: { ($0, $1) } ) } } final class ZipSink2_ : ZipSink { typealias R = O.E typealias Parent = Zip2 let _parent: Parent var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 2, observer: observer, cancel: cancel) } override func hasElements(_ index: Int) -> Bool { switch (index) { case 0: return _values1.count > 0 case 1: return _values2.count > 0 default: rxFatalError("Unhandled case (Function)") } return false } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) subscription1.setDisposable(_parent.source1.subscribe(observer1)) subscription2.setDisposable(_parent.source2.subscribe(observer2)) return Disposables.create([ subscription1, subscription2 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_values1.dequeue()!, _values2.dequeue()!) } } final class Zip2 : Producer { typealias ResultSelector = (E1, E2) throws -> R let source1: Observable let source2: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, resultSelector: @escaping ResultSelector) { self.source1 = source1 self.source2 = source2 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipSink2_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 3 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.E, O2.E, O3.E) throws -> E) -> Observable { return Zip3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3) -> Observable<(O1.E, O2.E, O3.E)> { return Zip3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), resultSelector: { ($0, $1, $2) } ) } } final class ZipSink3_ : ZipSink { typealias R = O.E typealias Parent = Zip3 let _parent: Parent var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) var _values3: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 3, observer: observer, cancel: cancel) } override func hasElements(_ index: Int) -> Bool { switch (index) { case 0: return _values1.count > 0 case 1: return _values2.count > 0 case 2: return _values3.count > 0 default: rxFatalError("Unhandled case (Function)") } return false } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) subscription1.setDisposable(_parent.source1.subscribe(observer1)) subscription2.setDisposable(_parent.source2.subscribe(observer2)) subscription3.setDisposable(_parent.source3.subscribe(observer3)) return Disposables.create([ subscription1, subscription2, subscription3 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_values1.dequeue()!, _values2.dequeue()!, _values3.dequeue()!) } } final class Zip3 : Producer { typealias ResultSelector = (E1, E2, E3) throws -> R let source1: Observable let source2: Observable let source3: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, resultSelector: @escaping ResultSelector) { self.source1 = source1 self.source2 = source2 self.source3 = source3 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipSink3_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 4 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E) throws -> E) -> Observable { return Zip4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4) -> Observable<(O1.E, O2.E, O3.E, O4.E)> { return Zip4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), resultSelector: { ($0, $1, $2, $3) } ) } } final class ZipSink4_ : ZipSink { typealias R = O.E typealias Parent = Zip4 let _parent: Parent var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) var _values3: Queue = Queue(capacity: 2) var _values4: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 4, observer: observer, cancel: cancel) } override func hasElements(_ index: Int) -> Bool { switch (index) { case 0: return _values1.count > 0 case 1: return _values2.count > 0 case 2: return _values3.count > 0 case 3: return _values4.count > 0 default: rxFatalError("Unhandled case (Function)") } return false } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) subscription1.setDisposable(_parent.source1.subscribe(observer1)) subscription2.setDisposable(_parent.source2.subscribe(observer2)) subscription3.setDisposable(_parent.source3.subscribe(observer3)) subscription4.setDisposable(_parent.source4.subscribe(observer4)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_values1.dequeue()!, _values2.dequeue()!, _values3.dequeue()!, _values4.dequeue()!) } } final class Zip4 : Producer { typealias ResultSelector = (E1, E2, E3, E4) throws -> R let source1: Observable let source2: Observable let source3: Observable let source4: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, resultSelector: @escaping ResultSelector) { self.source1 = source1 self.source2 = source2 self.source3 = source3 self.source4 = source4 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipSink4_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 5 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> E) -> Observable { return Zip5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E)> { return Zip5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), resultSelector: { ($0, $1, $2, $3, $4) } ) } } final class ZipSink5_ : ZipSink { typealias R = O.E typealias Parent = Zip5 let _parent: Parent var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) var _values3: Queue = Queue(capacity: 2) var _values4: Queue = Queue(capacity: 2) var _values5: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 5, observer: observer, cancel: cancel) } override func hasElements(_ index: Int) -> Bool { switch (index) { case 0: return _values1.count > 0 case 1: return _values2.count > 0 case 2: return _values3.count > 0 case 3: return _values4.count > 0 case 4: return _values5.count > 0 default: rxFatalError("Unhandled case (Function)") } return false } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) subscription1.setDisposable(_parent.source1.subscribe(observer1)) subscription2.setDisposable(_parent.source2.subscribe(observer2)) subscription3.setDisposable(_parent.source3.subscribe(observer3)) subscription4.setDisposable(_parent.source4.subscribe(observer4)) subscription5.setDisposable(_parent.source5.subscribe(observer5)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_values1.dequeue()!, _values2.dequeue()!, _values3.dequeue()!, _values4.dequeue()!, _values5.dequeue()!) } } final class Zip5 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> R let source1: Observable let source2: Observable let source3: Observable let source4: Observable let source5: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, resultSelector: @escaping ResultSelector) { self.source1 = source1 self.source2 = source2 self.source3 = source3 self.source4 = source4 self.source5 = source5 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipSink5_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 6 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> E) -> Observable { return Zip6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E, O6.E)> { return Zip6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), resultSelector: { ($0, $1, $2, $3, $4, $5) } ) } } final class ZipSink6_ : ZipSink { typealias R = O.E typealias Parent = Zip6 let _parent: Parent var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) var _values3: Queue = Queue(capacity: 2) var _values4: Queue = Queue(capacity: 2) var _values5: Queue = Queue(capacity: 2) var _values6: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 6, observer: observer, cancel: cancel) } override func hasElements(_ index: Int) -> Bool { switch (index) { case 0: return _values1.count > 0 case 1: return _values2.count > 0 case 2: return _values3.count > 0 case 3: return _values4.count > 0 case 4: return _values5.count > 0 case 5: return _values6.count > 0 default: rxFatalError("Unhandled case (Function)") } return false } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) let observer6 = ZipObserver(lock: _lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) subscription1.setDisposable(_parent.source1.subscribe(observer1)) subscription2.setDisposable(_parent.source2.subscribe(observer2)) subscription3.setDisposable(_parent.source3.subscribe(observer3)) subscription4.setDisposable(_parent.source4.subscribe(observer4)) subscription5.setDisposable(_parent.source5.subscribe(observer5)) subscription6.setDisposable(_parent.source6.subscribe(observer6)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5, subscription6 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_values1.dequeue()!, _values2.dequeue()!, _values3.dequeue()!, _values4.dequeue()!, _values5.dequeue()!, _values6.dequeue()!) } } final class Zip6 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> R let source1: Observable let source2: Observable let source3: Observable let source4: Observable let source5: Observable let source6: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, resultSelector: @escaping ResultSelector) { self.source1 = source1 self.source2 = source2 self.source3 = source3 self.source4 = source4 self.source5 = source5 self.source6 = source6 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipSink6_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 7 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> E) -> Observable { return Zip7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E)> { return Zip7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), resultSelector: { ($0, $1, $2, $3, $4, $5, $6) } ) } } final class ZipSink7_ : ZipSink { typealias R = O.E typealias Parent = Zip7 let _parent: Parent var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) var _values3: Queue = Queue(capacity: 2) var _values4: Queue = Queue(capacity: 2) var _values5: Queue = Queue(capacity: 2) var _values6: Queue = Queue(capacity: 2) var _values7: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 7, observer: observer, cancel: cancel) } override func hasElements(_ index: Int) -> Bool { switch (index) { case 0: return _values1.count > 0 case 1: return _values2.count > 0 case 2: return _values3.count > 0 case 3: return _values4.count > 0 case 4: return _values5.count > 0 case 5: return _values6.count > 0 case 6: return _values7.count > 0 default: rxFatalError("Unhandled case (Function)") } return false } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) let observer6 = ZipObserver(lock: _lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) let observer7 = ZipObserver(lock: _lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) subscription1.setDisposable(_parent.source1.subscribe(observer1)) subscription2.setDisposable(_parent.source2.subscribe(observer2)) subscription3.setDisposable(_parent.source3.subscribe(observer3)) subscription4.setDisposable(_parent.source4.subscribe(observer4)) subscription5.setDisposable(_parent.source5.subscribe(observer5)) subscription6.setDisposable(_parent.source6.subscribe(observer6)) subscription7.setDisposable(_parent.source7.subscribe(observer7)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5, subscription6, subscription7 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_values1.dequeue()!, _values2.dequeue()!, _values3.dequeue()!, _values4.dequeue()!, _values5.dequeue()!, _values6.dequeue()!, _values7.dequeue()!) } } final class Zip7 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> R let source1: Observable let source2: Observable let source3: Observable let source4: Observable let source5: Observable let source6: Observable let source7: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, resultSelector: @escaping ResultSelector) { self.source1 = source1 self.source2 = source2 self.source3 = source3 self.source4 = source4 self.source5 = source5 self.source6 = source6 self.source7 = source7 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipSink7_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } // 8 extension ObservableType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> E) -> Observable { return Zip8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), resultSelector: resultSelector ) } } extension ObservableType where E == Any { /** Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index. - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html) - returns: An observable sequence containing the result of combining elements of the sources. */ public static func zip (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8) -> Observable<(O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E)> { return Zip8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), resultSelector: { ($0, $1, $2, $3, $4, $5, $6, $7) } ) } } final class ZipSink8_ : ZipSink { typealias R = O.E typealias Parent = Zip8 let _parent: Parent var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) var _values3: Queue = Queue(capacity: 2) var _values4: Queue = Queue(capacity: 2) var _values5: Queue = Queue(capacity: 2) var _values6: Queue = Queue(capacity: 2) var _values7: Queue = Queue(capacity: 2) var _values8: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Cancelable) { _parent = parent super.init(arity: 8, observer: observer, cancel: cancel) } override func hasElements(_ index: Int) -> Bool { switch (index) { case 0: return _values1.count > 0 case 1: return _values2.count > 0 case 2: return _values3.count > 0 case 3: return _values4.count > 0 case 4: return _values5.count > 0 case 5: return _values6.count > 0 case 6: return _values7.count > 0 case 7: return _values8.count > 0 default: rxFatalError("Unhandled case (Function)") } return false } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() let subscription8 = SingleAssignmentDisposable() let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) let observer6 = ZipObserver(lock: _lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) let observer7 = ZipObserver(lock: _lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) let observer8 = ZipObserver(lock: _lock, parent: self, index: 7, setNextValue: { self._values8.enqueue($0) }, this: subscription8) subscription1.setDisposable(_parent.source1.subscribe(observer1)) subscription2.setDisposable(_parent.source2.subscribe(observer2)) subscription3.setDisposable(_parent.source3.subscribe(observer3)) subscription4.setDisposable(_parent.source4.subscribe(observer4)) subscription5.setDisposable(_parent.source5.subscribe(observer5)) subscription6.setDisposable(_parent.source6.subscribe(observer6)) subscription7.setDisposable(_parent.source7.subscribe(observer7)) subscription8.setDisposable(_parent.source8.subscribe(observer8)) return Disposables.create([ subscription1, subscription2, subscription3, subscription4, subscription5, subscription6, subscription7, subscription8 ]) } override func getResult() throws -> R { return try _parent._resultSelector(_values1.dequeue()!, _values2.dequeue()!, _values3.dequeue()!, _values4.dequeue()!, _values5.dequeue()!, _values6.dequeue()!, _values7.dequeue()!, _values8.dequeue()!) } } final class Zip8 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> R let source1: Observable let source2: Observable let source3: Observable let source4: Observable let source5: Observable let source6: Observable let source7: Observable let source8: Observable let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, source8: Observable, resultSelector: @escaping ResultSelector) { self.source1 = source1 self.source2 = source2 self.source3 = source3 self.source4 = source4 self.source5 = source5 self.source6 = source6 self.source7 = source7 self.source8 = source8 _resultSelector = resultSelector } override func run(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == R { let sink = ZipSink8_(parent: self, observer: observer, cancel: cancel) let subscription = sink.run() return (sink: sink, subscription: subscription) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observables/Zip.swift ================================================ // // Zip.swift // RxSwift // // Created by Krunoslav Zaher on 5/23/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol ZipSinkProtocol : class { func next(_ index: Int) func fail(_ error: Swift.Error) func done(_ index: Int) } class ZipSink : Sink, ZipSinkProtocol { typealias Element = O.E let _arity: Int let _lock = RecursiveLock() // state private var _isDone: [Bool] init(arity: Int, observer: O, cancel: Cancelable) { _isDone = [Bool](repeating: false, count: arity) _arity = arity super.init(observer: observer, cancel: cancel) } func getResult() throws -> Element { rxAbstractMethod() } func hasElements(_ index: Int) -> Bool { rxAbstractMethod() } func next(_ index: Int) { var hasValueAll = true for i in 0 ..< _arity { if !hasElements(i) { hasValueAll = false break } } if hasValueAll { do { let result = try getResult() self.forwardOn(.next(result)) } catch let e { self.forwardOn(.error(e)) dispose() } } else { var allOthersDone = true let arity = _isDone.count for i in 0 ..< arity { if i != index && !_isDone[i] { allOthersDone = false break } } if allOthersDone { forwardOn(.completed) self.dispose() } } } func fail(_ error: Swift.Error) { forwardOn(.error(error)) dispose() } func done(_ index: Int) { _isDone[index] = true var allDone = true for done in _isDone { if !done { allDone = false break } } if allDone { forwardOn(.completed) dispose() } } } final class ZipObserver : ObserverType , LockOwnerType , SynchronizedOnType { typealias E = ElementType typealias ValueSetter = (ElementType) -> () private var _parent: ZipSinkProtocol? let _lock: RecursiveLock // state private let _index: Int private let _this: Disposable private let _setNextValue: ValueSetter init(lock: RecursiveLock, parent: ZipSinkProtocol, index: Int, setNextValue: @escaping ValueSetter, this: Disposable) { _lock = lock _parent = parent _index = index _this = this _setNextValue = setNextValue } func on(_ event: Event) { synchronizedOn(event) } func _synchronized_on(_ event: Event) { if let _ = _parent { switch event { case .next(_): break case .error(_): _this.dispose() case .completed: _this.dispose() } } if let parent = _parent { switch event { case .next(let value): _setNextValue(value) parent.next(_index) case .error(let error): parent.fail(error) case .completed: parent.done(_index) } } } } ================================================ FILE: Pods/RxSwift/RxSwift/ObserverType.swift ================================================ // // ObserverType.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Supports push-style iteration over an observable sequence. public protocol ObserverType { /// The type of elements in sequence that observer can observe. associatedtype E /// Notify observer about sequence event. /// /// - parameter event: Event that occurred. func on(_ event: Event) } /// Convenience API extensions to provide alternate next, error, completed events extension ObserverType { /// Convenience method equivalent to `on(.next(element: E))` /// /// - parameter element: Next element to send to observer(s) public func onNext(_ element: E) { on(.next(element)) } /// Convenience method equivalent to `on(.completed)` public func onCompleted() { on(.completed) } /// Convenience method equivalent to `on(.error(Swift.Error))` /// - parameter error: Swift.Error to send to observer(s) public func onError(_ error: Swift.Error) { on(.error(error)) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observers/AnonymousObserver.swift ================================================ // // AnonymousObserver.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // final class AnonymousObserver : ObserverBase { typealias Element = ElementType typealias EventHandler = (Event) -> Void private let _eventHandler : EventHandler init(_ eventHandler: @escaping EventHandler) { #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif _eventHandler = eventHandler } override func onCore(_ event: Event) { return _eventHandler(event) } #if TRACE_RESOURCES deinit { let _ = Resources.decrementTotal() } #endif } ================================================ FILE: Pods/RxSwift/RxSwift/Observers/ObserverBase.swift ================================================ // // ObserverBase.swift // RxSwift // // Created by Krunoslav Zaher on 2/15/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // class ObserverBase : Disposable, ObserverType { typealias E = ElementType private var _isStopped: AtomicInt = 0 func on(_ event: Event) { switch event { case .next: if _isStopped == 0 { onCore(event) } case .error, .completed: if AtomicCompareAndSwap(0, 1, &_isStopped) { onCore(event) } } } func onCore(_ event: Event) { rxAbstractMethod() } func dispose() { _ = AtomicCompareAndSwap(0, 1, &_isStopped) } } ================================================ FILE: Pods/RxSwift/RxSwift/Observers/TailRecursiveSink.swift ================================================ // // TailRecursiveSink.swift // RxSwift // // Created by Krunoslav Zaher on 3/21/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // enum TailRecursiveSinkCommand { case moveNext case dispose } #if DEBUG || TRACE_RESOURCES public var maxTailRecursiveSinkStackSize = 0 #endif /// This class is usually used with `Generator` version of the operators. class TailRecursiveSink : Sink , InvocableWithValueType where S.Iterator.Element: ObservableConvertibleType, S.Iterator.Element.E == O.E { typealias Value = TailRecursiveSinkCommand typealias E = O.E typealias SequenceGenerator = (generator: S.Iterator, remaining: IntMax?) var _generators: [SequenceGenerator] = [] var _isDisposed = false var _subscription = SerialDisposable() // this is thread safe object var _gate = AsyncLock>>() override init(observer: O, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) } func run(_ sources: SequenceGenerator) -> Disposable { _generators.append(sources) schedule(.moveNext) return _subscription } func invoke(_ command: TailRecursiveSinkCommand) { switch command { case .dispose: disposeCommand() case .moveNext: moveNextCommand() } } // simple implementation for now func schedule(_ command: TailRecursiveSinkCommand) { _gate.invoke(InvocableScheduledItem(invocable: self, state: command)) } func done() { forwardOn(.completed) dispose() } func extract(_ observable: Observable) -> SequenceGenerator? { rxAbstractMethod() } // should be done on gate locked private func moveNextCommand() { var next: Observable? = nil repeat { guard let (g, left) = _generators.last else { break } if _isDisposed { return } _generators.removeLast() var e = g guard let nextCandidate = e.next()?.asObservable() else { continue } // `left` is a hint of how many elements are left in generator. // In case this is the last element, then there is no need to push // that generator on stack. // // This is an optimization used to make sure in tail recursive case // there is no memory leak in case this operator is used to generate non terminating // sequence. if let knownOriginalLeft = left { // `- 1` because generator.next() has just been called if knownOriginalLeft - 1 >= 1 { _generators.append((e, knownOriginalLeft - 1)) } } else { _generators.append((e, nil)) } let nextGenerator = extract(nextCandidate) if let nextGenerator = nextGenerator { _generators.append(nextGenerator) #if DEBUG || TRACE_RESOURCES if maxTailRecursiveSinkStackSize < _generators.count { maxTailRecursiveSinkStackSize = _generators.count } #endif } else { next = nextCandidate } } while next == nil guard let existingNext = next else { done() return } let disposable = SingleAssignmentDisposable() _subscription.disposable = disposable disposable.setDisposable(subscribeToNext(existingNext)) } func subscribeToNext(_ source: Observable) -> Disposable { rxAbstractMethod() } func disposeCommand() { _isDisposed = true _generators.removeAll(keepingCapacity: false) } override func dispose() { super.dispose() _subscription.dispose() _gate.dispose() schedule(.dispose) } } ================================================ FILE: Pods/RxSwift/RxSwift/Reactive.swift ================================================ // // Reactive.swift // RxSwift // // Created by Yury Korolev on 5/2/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // /** Use `Reactive` proxy as customization point for constrained protocol extensions. General pattern would be: // 1. Extend Reactive protocol with constrain on Base // Read as: Reactive Extension where Base is a SomeType extension Reactive where Base: SomeType { // 2. Put any specific reactive extension for SomeType here } With this approach we can have more specialized methods and properties using `Base` and not just specialized on common base type. */ public struct Reactive { /// Base object to extend. public let base: Base /// Creates extensions with base object. /// /// - parameter base: Base object. public init(_ base: Base) { self.base = base } } /// A type that has reactive extensions. public protocol ReactiveCompatible { /// Extended type associatedtype CompatibleType /// Reactive extensions. static var rx: Reactive.Type { get set } /// Reactive extensions. var rx: Reactive { get set } } extension ReactiveCompatible { /// Reactive extensions. public static var rx: Reactive.Type { get { return Reactive.self } set { // this enables using Reactive to "mutate" base type } } /// Reactive extensions. public var rx: Reactive { get { return Reactive(self) } set { // this enables using Reactive to "mutate" base object } } } import class Foundation.NSObject /// Extend NSObject with `rx` proxy. extension NSObject: ReactiveCompatible { } ================================================ FILE: Pods/RxSwift/RxSwift/Rx.swift ================================================ // // Rx.swift // RxSwift // // Created by Krunoslav Zaher on 2/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if TRACE_RESOURCES fileprivate var resourceCount: AtomicInt = 0 /// Resource utilization information public struct Resources { /// Counts internal Rx resource allocations (Observables, Observers, Disposables, etc.). This provides a simple way to detect leaks during development. public static var total: Int32 { return resourceCount.valueSnapshot() } /// Increments `Resources.total` resource count. /// /// - returns: New resource count public static func incrementTotal() -> Int32 { return AtomicIncrement(&resourceCount) } /// Decrements `Resources.total` resource count /// /// - returns: New resource count public static func decrementTotal() -> Int32 { return AtomicDecrement(&resourceCount) } } #endif /// Swift does not implement abstract methods. This method is used as a runtime check to ensure that methods which intended to be abstract (i.e., they should be implemented in subclasses) are not called directly on the superclass. func rxAbstractMethod(file: StaticString = #file, line: UInt = #line) -> Swift.Never { rxFatalError("Abstract method", file: file, line: line) } func rxFatalError(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> Swift.Never { // The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours. fatalError(lastMessage(), file: file, line: line) } func rxFatalErrorInDebug(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) { #if DEBUG fatalError(lastMessage(), file: file, line: line) #else print("\(file):\(line): \(lastMessage())") #endif } func incrementChecked(_ i: inout Int) throws -> Int { if i == Int.max { throw RxError.overflow } defer { i += 1 } return i } func decrementChecked(_ i: inout Int) throws -> Int { if i == Int.min { throw RxError.overflow } defer { i -= 1 } return i } #if DEBUG import class Foundation.Thread final class SynchronizationTracker { private let _lock = RecursiveLock() public enum SychronizationErrorMessages: String { case variable = "Two different threads are trying to assign the same `Variable.value` unsynchronized.\n This is undefined behavior because the end result (variable value) is nondeterministic and depends on the \n operating system thread scheduler. This will cause random behavior of your program.\n" case `default` = "Two different unsynchronized threads are trying to send some event simultaneously.\n This is undefined behavior because the ordering of the effects caused by these events is nondeterministic and depends on the \n operating system thread scheduler. This will result in a random behavior of your program.\n" } private var _threads = Dictionary() private func synchronizationError(_ message: String) { #if FATAL_SYNCHRONIZATION rxFatalError(message) #else print(message) #endif } func register(synchronizationErrorMessage: SychronizationErrorMessages) { _lock.lock(); defer { _lock.unlock() } let pointer = Unmanaged.passUnretained(Thread.current).toOpaque() let count = (_threads[pointer] ?? 0) + 1 if count > 1 { synchronizationError( "⚠️ Reentrancy anomaly was detected. ⚠️\n" + " > Debugging: To debug this issue you can set a breakpoint in \(#file):\(#line) and observe the call stack.\n" + " > Problem: This behavior is breaking the observable sequence grammar. `next (error | completed)?`\n" + " This behavior breaks the grammar because there is overlapping between sequence events.\n" + " Observable sequence is trying to send an event before sending of previous event has finished.\n" + " > Interpretation: This could mean that there is some kind of unexpected cyclic dependency in your code,\n" + " or that the system is not behaving in the expected way.\n" + " > Remedy: If this is the expected behavior this message can be suppressed by adding `.observeOn(MainScheduler.asyncInstance)`\n" + " or by enqueing sequence events in some other way.\n" ) } _threads[pointer] = count if _threads.count > 1 { synchronizationError( "⚠️ Synchronization anomaly was detected. ⚠️\n" + " > Debugging: To debug this issue you can set a breakpoint in \(#file):\(#line) and observe the call stack.\n" + " > Problem: This behavior is breaking the observable sequence grammar. `next (error | completed)?`\n" + " This behavior breaks the grammar because there is overlapping between sequence events.\n" + " Observable sequence is trying to send an event before sending of previous event has finished.\n" + " > Interpretation: " + synchronizationErrorMessage.rawValue + " > Remedy: If this is the expected behavior this message can be suppressed by adding `.observeOn(MainScheduler.asyncInstance)`\n" + " or by synchronizing sequence events in some other way.\n" ) } } func unregister() { _lock.lock(); defer { _lock.unlock() } let pointer = Unmanaged.passUnretained(Thread.current).toOpaque() _threads[pointer] = (_threads[pointer] ?? 1) - 1 if _threads[pointer] == 0 { _threads[pointer] = nil } } } #endif /// RxSwift global hooks public enum Hooks { } ================================================ FILE: Pods/RxSwift/RxSwift/RxMutableBox.swift ================================================ // // RxMutableBox.swift // RxSwift // // Created by Krunoslav Zaher on 5/22/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Creates mutable reference wrapper for any type. final class RxMutableBox : CustomDebugStringConvertible { /// Wrapped value var value : T /// Creates reference wrapper for `value`. /// /// - parameter value: Value to wrap. init (_ value: T) { self.value = value } } extension RxMutableBox { /// - returns: Box description. var debugDescription: String { return "MutatingBox(\(self.value))" } } ================================================ FILE: Pods/RxSwift/RxSwift/SchedulerType.swift ================================================ // // SchedulerType.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.TimeInterval import struct Foundation.Date // Type that represents time interval in the context of RxSwift. public typealias RxTimeInterval = TimeInterval /// Type that represents absolute time in the context of RxSwift. public typealias RxTime = Date /// Represents an object that schedules units of work. public protocol SchedulerType: ImmediateSchedulerType { /// - returns: Current time. var now : RxTime { get } /** Schedules an action to be executed. - parameter state: State passed to the action to be executed. - parameter dueTime: Relative time after which to execute the action. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable /** Schedules a periodic piece of work. - parameter state: State passed to the action to be executed. - parameter startAfter: Period after which initial work should be run. - parameter period: Period for running the work periodically. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable } extension SchedulerType { /** Periodic task will be emulated using recursive scheduling. - parameter state: Initial state passed to the action upon the first iteration. - parameter startAfter: Period after which initial work should be run. - parameter period: Period for running the work periodically. - returns: The disposable object used to cancel the scheduled recurring action (best effort). */ public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { let schedule = SchedulePeriodicRecursive(scheduler: self, startAfter: startAfter, period: period, action: action, state: state) return schedule.start() } func scheduleRecursive(_ state: State, dueTime: RxTimeInterval, action: @escaping (State, AnyRecursiveScheduler) -> ()) -> Disposable { let scheduler = AnyRecursiveScheduler(scheduler: self, action: action) scheduler.schedule(state, dueTime: dueTime) return Disposables.create(with: scheduler.dispose) } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift ================================================ // // ConcurrentDispatchQueueScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 7/5/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.Date import struct Foundation.TimeInterval import Dispatch /// Abstracts the work that needs to be performed on a specific `dispatch_queue_t`. You can also pass a serial dispatch queue, it shouldn't cause any problems. /// /// This scheduler is suitable when some work needs to be performed in background. public class ConcurrentDispatchQueueScheduler: SchedulerType { public typealias TimeInterval = Foundation.TimeInterval public typealias Time = Date public var now : Date { return Date() } let configuration: DispatchQueueConfiguration /// Constructs new `ConcurrentDispatchQueueScheduler` that wraps `queue`. /// /// - parameter queue: Target dispatch queue. public init(queue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) { configuration = DispatchQueueConfiguration(queue: queue, leeway: leeway) } /// Convenience init for scheduler that wraps one of the global concurrent dispatch queues. /// /// - parameter qos: Target global dispatch queue, by quality of service class. @available(iOS 8, OSX 10.10, *) public convenience init(qos: DispatchQoS, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) { self.init(queue: DispatchQueue( label: "rxswift.queue.\(qos)", qos: qos, attributes: [DispatchQueue.Attributes.concurrent], target: nil), leeway: leeway ) } /** Schedules an action to be executed immediately. - parameter state: State passed to the action to be executed. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public final func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { return self.configuration.schedule(state, action: action) } /** Schedules an action to be executed. - parameter state: State passed to the action to be executed. - parameter dueTime: Relative time after which to execute the action. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public final func scheduleRelative(_ state: StateType, dueTime: Foundation.TimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { return self.configuration.scheduleRelative(state, dueTime: dueTime, action: action) } /** Schedules a periodic piece of work. - parameter state: State passed to the action to be executed. - parameter startAfter: Period after which initial work should be run. - parameter period: Period for running the work periodically. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedulePeriodic(_ state: StateType, startAfter: TimeInterval, period: TimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action) } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/ConcurrentMainScheduler.swift ================================================ // // ConcurrentMainScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 10/17/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.Date import struct Foundation.TimeInterval import Dispatch /** Abstracts work that needs to be performed on `MainThread`. In case `schedule` methods are called from main thread, it will perform action immediately without scheduling. This scheduler is optimized for `subscribeOn` operator. If you want to observe observable sequence elements on main thread using `observeOn` operator, `MainScheduler` is more suitable for that purpose. */ public final class ConcurrentMainScheduler : SchedulerType { public typealias TimeInterval = Foundation.TimeInterval public typealias Time = Date private let _mainScheduler: MainScheduler private let _mainQueue: DispatchQueue /// - returns: Current time. public var now : Date { return _mainScheduler.now as Date } private init(mainScheduler: MainScheduler) { _mainQueue = DispatchQueue.main _mainScheduler = mainScheduler } /// Singleton instance of `ConcurrentMainScheduler` public static let instance = ConcurrentMainScheduler(mainScheduler: MainScheduler.instance) /** Schedules an action to be executed immediately. - parameter state: State passed to the action to be executed. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { if DispatchQueue.isMain { return action(state) } let cancel = SingleAssignmentDisposable() _mainQueue.async { if cancel.isDisposed { return } cancel.setDisposable(action(state)) } return cancel } /** Schedules an action to be executed. - parameter state: State passed to the action to be executed. - parameter dueTime: Relative time after which to execute the action. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public final func scheduleRelative(_ state: StateType, dueTime: Foundation.TimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { return _mainScheduler.scheduleRelative(state, dueTime: dueTime, action: action) } /** Schedules a periodic piece of work. - parameter state: State passed to the action to be executed. - parameter startAfter: Period after which initial work should be run. - parameter period: Period for running the work periodically. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedulePeriodic(_ state: StateType, startAfter: TimeInterval, period: TimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { return _mainScheduler.schedulePeriodic(state, startAfter: startAfter, period: period, action: action) } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/CurrentThreadScheduler.swift ================================================ // // CurrentThreadScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 8/30/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import class Foundation.NSObject import protocol Foundation.NSCopying import class Foundation.Thread import Dispatch #if os(Linux) import struct Foundation.pthread_key_t import func Foundation.pthread_setspecific import func Foundation.pthread_getspecific import func Foundation.pthread_key_create fileprivate enum CurrentThreadSchedulerQueueKey { fileprivate static let instance = "RxSwift.CurrentThreadScheduler.Queue" } #else fileprivate class CurrentThreadSchedulerQueueKey: NSObject, NSCopying { static let instance = CurrentThreadSchedulerQueueKey() private override init() { super.init() } override var hash: Int { return 0 } public func copy(with zone: NSZone? = nil) -> Any { return self } } #endif /// Represents an object that schedules units of work on the current thread. /// /// This is the default scheduler for operators that generate elements. /// /// This scheduler is also sometimes called `trampoline scheduler`. public class CurrentThreadScheduler : ImmediateSchedulerType { typealias ScheduleQueue = RxMutableBox> /// The singleton instance of the current thread scheduler. public static let instance = CurrentThreadScheduler() private static var isScheduleRequiredKey: pthread_key_t = { () -> pthread_key_t in let key = UnsafeMutablePointer.allocate(capacity: 1) defer { key.deallocate(capacity: 1) } guard pthread_key_create(key, nil) == 0 else { rxFatalError("isScheduleRequired key creation failed") } return key.pointee }() private static var scheduleInProgressSentinel: UnsafeRawPointer = { () -> UnsafeRawPointer in return UnsafeRawPointer(UnsafeMutablePointer.allocate(capacity: 1)) }() static var queue : ScheduleQueue? { get { return Thread.getThreadLocalStorageValueForKey(CurrentThreadSchedulerQueueKey.instance) } set { Thread.setThreadLocalStorageValue(newValue, forKey: CurrentThreadSchedulerQueueKey.instance) } } /// Gets a value that indicates whether the caller must call a `schedule` method. public static fileprivate(set) var isScheduleRequired: Bool { get { return pthread_getspecific(CurrentThreadScheduler.isScheduleRequiredKey) == nil } set(isScheduleRequired) { if pthread_setspecific(CurrentThreadScheduler.isScheduleRequiredKey, isScheduleRequired ? nil : scheduleInProgressSentinel) != 0 { rxFatalError("pthread_setspecific failed") } } } /** Schedules an action to be executed as soon as possible on current thread. If this method is called on some thread that doesn't have `CurrentThreadScheduler` installed, scheduler will be automatically installed and uninstalled after all work is performed. - parameter state: State passed to the action to be executed. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { if CurrentThreadScheduler.isScheduleRequired { CurrentThreadScheduler.isScheduleRequired = false let disposable = action(state) defer { CurrentThreadScheduler.isScheduleRequired = true CurrentThreadScheduler.queue = nil } guard let queue = CurrentThreadScheduler.queue else { return disposable } while let latest = queue.value.dequeue() { if latest.isDisposed { continue } latest.invoke() } return disposable } let existingQueue = CurrentThreadScheduler.queue let queue: RxMutableBox> if let existingQueue = existingQueue { queue = existingQueue } else { queue = RxMutableBox(Queue(capacity: 1)) CurrentThreadScheduler.queue = queue } let scheduledItem = ScheduledItem(action: action, state: state) queue.value.enqueue(scheduledItem) return scheduledItem } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/HistoricalScheduler.swift ================================================ // // HistoricalScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 12/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.Date /// Provides a virtual time scheduler that uses `Date` for absolute time and `NSTimeInterval` for relative time. public class HistoricalScheduler : VirtualTimeScheduler { /** Creates a new historical scheduler with initial clock value. - parameter initialClock: Initial value for virtual clock. */ public init(initialClock: RxTime = Date(timeIntervalSince1970: 0)) { super.init(initialClock: initialClock, converter: HistoricalSchedulerTimeConverter()) } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/HistoricalSchedulerTimeConverter.swift ================================================ // // HistoricalSchedulerTimeConverter.swift // RxSwift // // Created by Krunoslav Zaher on 12/27/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.Date /// Converts historial virtual time into real time. /// /// Since historical virtual time is also measured in `Date`, this converter is identity function. public struct HistoricalSchedulerTimeConverter : VirtualTimeConverterType { /// Virtual time unit used that represents ticks of virtual clock. public typealias VirtualTimeUnit = RxTime /// Virtual time unit used to represent differences of virtual times. public typealias VirtualTimeIntervalUnit = RxTimeInterval /// Returns identical value of argument passed because historical virtual time is equal to real time, just /// decoupled from local machine clock. public func convertFromVirtualTime(_ virtualTime: VirtualTimeUnit) -> RxTime { return virtualTime } /// Returns identical value of argument passed because historical virtual time is equal to real time, just /// decoupled from local machine clock. public func convertToVirtualTime(_ time: RxTime) -> VirtualTimeUnit { return time } /// Returns identical value of argument passed because historical virtual time is equal to real time, just /// decoupled from local machine clock. public func convertFromVirtualTimeInterval(_ virtualTimeInterval: VirtualTimeIntervalUnit) -> RxTimeInterval { return virtualTimeInterval } /// Returns identical value of argument passed because historical virtual time is equal to real time, just /// decoupled from local machine clock. public func convertToVirtualTimeInterval(_ timeInterval: RxTimeInterval) -> VirtualTimeIntervalUnit { return timeInterval } /** Offsets `Date` by time interval. - parameter time: Time. - parameter timeInterval: Time interval offset. - returns: Time offsetted by time interval. */ public func offsetVirtualTime(_ time: VirtualTimeUnit, offset: VirtualTimeIntervalUnit) -> VirtualTimeUnit { return time.addingTimeInterval(offset) } /// Compares two `Date`s. public func compareVirtualTime(_ lhs: VirtualTimeUnit, _ rhs: VirtualTimeUnit) -> VirtualTimeComparison { switch lhs.compare(rhs as Date) { case .orderedAscending: return .lessThan case .orderedSame: return .equal case .orderedDescending: return .greaterThan } } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift ================================================ // // DispatchQueueConfiguration.swift // RxSwift // // Created by Krunoslav Zaher on 7/23/16. // Copyright © 2016 Krunoslav Zaher. All rights reserved. // import Dispatch import struct Foundation.TimeInterval struct DispatchQueueConfiguration { let queue: DispatchQueue let leeway: DispatchTimeInterval } private func dispatchInterval(_ interval: Foundation.TimeInterval) -> DispatchTimeInterval { precondition(interval >= 0.0) // TODO: Replace 1000 with something that actually works // NSEC_PER_MSEC returns 1000000 return DispatchTimeInterval.milliseconds(Int(interval * 1000.0)) } extension DispatchQueueConfiguration { func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { let cancel = SingleAssignmentDisposable() queue.async { if cancel.isDisposed { return } cancel.setDisposable(action(state)) } return cancel } func scheduleRelative(_ state: StateType, dueTime: Foundation.TimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { let deadline = DispatchTime.now() + dispatchInterval(dueTime) let compositeDisposable = CompositeDisposable() let timer = DispatchSource.makeTimerSource(queue: queue) #if swift(>=4.0) timer.schedule(deadline: deadline, leeway: leeway) #else timer.scheduleOneshot(deadline: deadline, leeway: leeway) #endif // TODO: // This looks horrible, and yes, it is. // It looks like Apple has made a conceputal change here, and I'm unsure why. // Need more info on this. // It looks like just setting timer to fire and not holding a reference to it // until deadline causes timer cancellation. var timerReference: DispatchSourceTimer? = timer let cancelTimer = Disposables.create { timerReference?.cancel() timerReference = nil } timer.setEventHandler(handler: { if compositeDisposable.isDisposed { return } _ = compositeDisposable.insert(action(state)) cancelTimer.dispose() }) timer.resume() _ = compositeDisposable.insert(cancelTimer) return compositeDisposable } func schedulePeriodic(_ state: StateType, startAfter: TimeInterval, period: TimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { let initial = DispatchTime.now() + dispatchInterval(startAfter) var timerState = state let timer = DispatchSource.makeTimerSource(queue: queue) #if swift(>=4.0) timer.schedule(deadline: initial, repeating: dispatchInterval(period), leeway: leeway) #else timer.scheduleRepeating(deadline: initial, interval: dispatchInterval(period), leeway: leeway) #endif // TODO: // This looks horrible, and yes, it is. // It looks like Apple has made a conceputal change here, and I'm unsure why. // Need more info on this. // It looks like just setting timer to fire and not holding a reference to it // until deadline causes timer cancellation. var timerReference: DispatchSourceTimer? = timer let cancelTimer = Disposables.create { timerReference?.cancel() timerReference = nil } timer.setEventHandler(handler: { if cancelTimer.isDisposed { return } timerState = action(timerState) }) timer.resume() return cancelTimer } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/Internal/InvocableScheduledItem.swift ================================================ // // InvocableScheduledItem.swift // RxSwift // // Created by Krunoslav Zaher on 11/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // struct InvocableScheduledItem : InvocableType { let _invocable: I let _state: I.Value init(invocable: I, state: I.Value) { _invocable = invocable _state = state } func invoke() { _invocable.invoke(_state) } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/Internal/InvocableType.swift ================================================ // // InvocableType.swift // RxSwift // // Created by Krunoslav Zaher on 11/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol InvocableType { func invoke() } protocol InvocableWithValueType { associatedtype Value func invoke(_ value: Value) } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/Internal/ScheduledItem.swift ================================================ // // ScheduledItem.swift // RxSwift // // Created by Krunoslav Zaher on 9/2/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // struct ScheduledItem : ScheduledItemType , InvocableType { typealias Action = (T) -> Disposable private let _action: Action private let _state: T private let _disposable = SingleAssignmentDisposable() var isDisposed: Bool { return _disposable.isDisposed } init(action: @escaping Action, state: T) { _action = action _state = state } func invoke() { _disposable.setDisposable(_action(_state)) } func dispose() { _disposable.dispose() } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/Internal/ScheduledItemType.swift ================================================ // // ScheduledItemType.swift // RxSwift // // Created by Krunoslav Zaher on 11/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // protocol ScheduledItemType : Cancelable , InvocableType { func invoke() } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/MainScheduler.swift ================================================ // // MainScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import Dispatch /** Abstracts work that needs to be performed on `DispatchQueue.main`. In case `schedule` methods are called from `DispatchQueue.main`, it will perform action immediately without scheduling. This scheduler is usually used to perform UI work. Main scheduler is a specialization of `SerialDispatchQueueScheduler`. This scheduler is optimized for `observeOn` operator. To ensure observable sequence is subscribed on main thread using `subscribeOn` operator please use `ConcurrentMainScheduler` because it is more optimized for that purpose. */ public final class MainScheduler : SerialDispatchQueueScheduler { private let _mainQueue: DispatchQueue var numberEnqueued: AtomicInt = 0 /// Initializes new instance of `MainScheduler`. public init() { _mainQueue = DispatchQueue.main super.init(serialQueue: _mainQueue) } /// Singleton instance of `MainScheduler` public static let instance = MainScheduler() /// Singleton instance of `MainScheduler` that always schedules work asynchronously /// and doesn't perform optimizations for calls scheduled from main queue. public static let asyncInstance = SerialDispatchQueueScheduler(serialQueue: DispatchQueue.main) /// In case this method is called on a background thread it will throw an exception. public class func ensureExecutingOnScheduler(errorMessage: String? = nil) { if !DispatchQueue.isMain { rxFatalError(errorMessage ?? "Executing on backgound thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread.") } } override func scheduleInternal(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { let currentNumberEnqueued = AtomicIncrement(&numberEnqueued) if DispatchQueue.isMain && currentNumberEnqueued == 1 { let disposable = action(state) _ = AtomicDecrement(&numberEnqueued) return disposable } let cancel = SingleAssignmentDisposable() _mainQueue.async { if !cancel.isDisposed { _ = action(state) } _ = AtomicDecrement(&self.numberEnqueued) } return cancel } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/OperationQueueScheduler.swift ================================================ // // OperationQueueScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 4/4/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import class Foundation.OperationQueue import class Foundation.BlockOperation import Dispatch /// Abstracts the work that needs to be performed on a specific `NSOperationQueue`. /// /// This scheduler is suitable for cases when there is some bigger chunk of work that needs to be performed in background and you want to fine tune concurrent processing using `maxConcurrentOperationCount`. public class OperationQueueScheduler: ImmediateSchedulerType { public let operationQueue: OperationQueue /// Constructs new instance of `OperationQueueScheduler` that performs work on `operationQueue`. /// /// - parameter operationQueue: Operation queue targeted to perform work on. public init(operationQueue: OperationQueue) { self.operationQueue = operationQueue } /** Schedules an action to be executed recursively. - parameter state: State passed to the action to be executed. - parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { let cancel = SingleAssignmentDisposable() let operation = BlockOperation { if cancel.isDisposed { return } cancel.setDisposable(action(state)) } self.operationQueue.addOperation(operation) return cancel } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/RecursiveScheduler.swift ================================================ // // RecursiveScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 6/7/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // fileprivate enum ScheduleState { case initial case added(CompositeDisposable.DisposeKey) case done } /// Type erased recursive scheduler. final class AnyRecursiveScheduler { typealias Action = (State, AnyRecursiveScheduler) -> Void private let _lock = RecursiveLock() // state private let _group = CompositeDisposable() private var _scheduler: SchedulerType private var _action: Action? init(scheduler: SchedulerType, action: @escaping Action) { _action = action _scheduler = scheduler } /** Schedules an action to be executed recursively. - parameter state: State passed to the action to be executed. - parameter dueTime: Relative time after which to execute the recursive action. */ func schedule(_ state: State, dueTime: RxTimeInterval) { var scheduleState: ScheduleState = .initial let d = _scheduler.scheduleRelative(state, dueTime: dueTime) { (state) -> Disposable in // best effort if self._group.isDisposed { return Disposables.create() } let action = self._lock.calculateLocked { () -> Action? in switch scheduleState { case let .added(removeKey): self._group.remove(for: removeKey) case .initial: break case .done: break } scheduleState = .done return self._action } if let action = action { action(state, self) } return Disposables.create() } _lock.performLocked { switch scheduleState { case .added: rxFatalError("Invalid state") break case .initial: if let removeKey = _group.insert(d) { scheduleState = .added(removeKey) } else { scheduleState = .done } break case .done: break } } } /// Schedules an action to be executed recursively. /// /// - parameter state: State passed to the action to be executed. func schedule(_ state: State) { var scheduleState: ScheduleState = .initial let d = _scheduler.schedule(state) { (state) -> Disposable in // best effort if self._group.isDisposed { return Disposables.create() } let action = self._lock.calculateLocked { () -> Action? in switch scheduleState { case let .added(removeKey): self._group.remove(for: removeKey) case .initial: break case .done: break } scheduleState = .done return self._action } if let action = action { action(state, self) } return Disposables.create() } _lock.performLocked { switch scheduleState { case .added: rxFatalError("Invalid state") break case .initial: if let removeKey = _group.insert(d) { scheduleState = .added(removeKey) } else { scheduleState = .done } break case .done: break } } } func dispose() { _lock.performLocked { _action = nil } _group.dispose() } } /// Type erased recursive scheduler. final class RecursiveImmediateScheduler { typealias Action = (_ state: State, _ recurse: (State) -> Void) -> Void private var _lock = SpinLock() private let _group = CompositeDisposable() private var _action: Action? private let _scheduler: ImmediateSchedulerType init(action: @escaping Action, scheduler: ImmediateSchedulerType) { _action = action _scheduler = scheduler } // immediate scheduling /// Schedules an action to be executed recursively. /// /// - parameter state: State passed to the action to be executed. func schedule(_ state: State) { var scheduleState: ScheduleState = .initial let d = _scheduler.schedule(state) { (state) -> Disposable in // best effort if self._group.isDisposed { return Disposables.create() } let action = self._lock.calculateLocked { () -> Action? in switch scheduleState { case let .added(removeKey): self._group.remove(for: removeKey) case .initial: break case .done: break } scheduleState = .done return self._action } if let action = action { action(state, self.schedule) } return Disposables.create() } _lock.performLocked { switch scheduleState { case .added: rxFatalError("Invalid state") break case .initial: if let removeKey = _group.insert(d) { scheduleState = .added(removeKey) } else { scheduleState = .done } break case .done: break } } } func dispose() { _lock.performLocked { _action = nil } _group.dispose() } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/SchedulerServices+Emulation.swift ================================================ // // SchedulerServices+Emulation.swift // RxSwift // // Created by Krunoslav Zaher on 6/6/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // enum SchedulePeriodicRecursiveCommand { case tick case dispatchStart } final class SchedulePeriodicRecursive { typealias RecursiveAction = (State) -> State typealias RecursiveScheduler = AnyRecursiveScheduler private let _scheduler: SchedulerType private let _startAfter: RxTimeInterval private let _period: RxTimeInterval private let _action: RecursiveAction private var _state: State private var _pendingTickCount: AtomicInt = 0 init(scheduler: SchedulerType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping RecursiveAction, state: State) { _scheduler = scheduler _startAfter = startAfter _period = period _action = action _state = state } func start() -> Disposable { return _scheduler.scheduleRecursive(SchedulePeriodicRecursiveCommand.tick, dueTime: _startAfter, action: self.tick) } func tick(_ command: SchedulePeriodicRecursiveCommand, scheduler: RecursiveScheduler) -> Void { // Tries to emulate periodic scheduling as best as possible. // The problem that could arise is if handling periodic ticks take too long, or // tick interval is short. switch command { case .tick: scheduler.schedule(.tick, dueTime: _period) // The idea is that if on tick there wasn't any item enqueued, schedule to perform work immediately. // Else work will be scheduled after previous enqueued work completes. if AtomicIncrement(&_pendingTickCount) == 1 { self.tick(.dispatchStart, scheduler: scheduler) } case .dispatchStart: _state = _action(_state) // Start work and schedule check is this last batch of work if AtomicDecrement(&_pendingTickCount) > 0 { // This gives priority to scheduler emulation, it's not perfect, but helps scheduler.schedule(SchedulePeriodicRecursiveCommand.dispatchStart) } } } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift ================================================ // // SerialDispatchQueueScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 2/8/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // import struct Foundation.TimeInterval import struct Foundation.Date import Dispatch /** Abstracts the work that needs to be performed on a specific `dispatch_queue_t`. It will make sure that even if concurrent dispatch queue is passed, it's transformed into a serial one. It is extremely important that this scheduler is serial, because certain operator perform optimizations that rely on that property. Because there is no way of detecting is passed dispatch queue serial or concurrent, for every queue that is being passed, worst case (concurrent) will be assumed, and internal serial proxy dispatch queue will be created. This scheduler can also be used with internal serial queue alone. In case some customization need to be made on it before usage, internal serial queue can be customized using `serialQueueConfiguration` callback. */ public class SerialDispatchQueueScheduler : SchedulerType { public typealias TimeInterval = Foundation.TimeInterval public typealias Time = Date /// - returns: Current time. public var now : Date { return Date() } let configuration: DispatchQueueConfiguration init(serialQueue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) { configuration = DispatchQueueConfiguration(queue: serialQueue, leeway: leeway) } /** Constructs new `SerialDispatchQueueScheduler` with internal serial queue named `internalSerialQueueName`. Additional dispatch queue properties can be set after dispatch queue is created using `serialQueueConfiguration`. - parameter internalSerialQueueName: Name of internal serial dispatch queue. - parameter serialQueueConfiguration: Additional configuration of internal serial dispatch queue. */ public convenience init(internalSerialQueueName: String, serialQueueConfiguration: ((DispatchQueue) -> Void)? = nil, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) { let queue = DispatchQueue(label: internalSerialQueueName, attributes: []) serialQueueConfiguration?(queue) self.init(serialQueue: queue, leeway: leeway) } /** Constructs new `SerialDispatchQueueScheduler` named `internalSerialQueueName` that wraps `queue`. - parameter queue: Possibly concurrent dispatch queue used to perform work. - parameter internalSerialQueueName: Name of internal serial dispatch queue proxy. */ public convenience init(queue: DispatchQueue, internalSerialQueueName: String, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) { // Swift 3.0 IUO let serialQueue = DispatchQueue(label: internalSerialQueueName, attributes: [], target: queue) self.init(serialQueue: serialQueue, leeway: leeway) } /** Constructs new `SerialDispatchQueueScheduler` that wraps on of the global concurrent dispatch queues. - parameter qos: Identifier for global dispatch queue with specified quality of service class. - parameter internalSerialQueueName: Custom name for internal serial dispatch queue proxy. */ @available(iOS 8, OSX 10.10, *) public convenience init(qos: DispatchQoS, internalSerialQueueName: String = "rx.global_dispatch_queue.serial", leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) { self.init(queue: DispatchQueue.global(qos: qos.qosClass), internalSerialQueueName: internalSerialQueueName, leeway: leeway) } /** Schedules an action to be executed immediately. - parameter state: State passed to the action to be executed. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public final func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { return self.scheduleInternal(state, action: action) } func scheduleInternal(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { return self.configuration.schedule(state, action: action) } /** Schedules an action to be executed. - parameter state: State passed to the action to be executed. - parameter dueTime: Relative time after which to execute the action. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public final func scheduleRelative(_ state: StateType, dueTime: Foundation.TimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { return self.configuration.scheduleRelative(state, dueTime: dueTime, action: action) } /** Schedules a periodic piece of work. - parameter state: State passed to the action to be executed. - parameter startAfter: Period after which initial work should be run. - parameter period: Period for running the work periodically. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedulePeriodic(_ state: StateType, startAfter: TimeInterval, period: TimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action) } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/VirtualTimeConverterType.swift ================================================ // // VirtualTimeConverterType.swift // RxSwift // // Created by Krunoslav Zaher on 12/23/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Parametrization for virtual time used by `VirtualTimeScheduler`s. public protocol VirtualTimeConverterType { /// Virtual time unit used that represents ticks of virtual clock. associatedtype VirtualTimeUnit /// Virtual time unit used to represent differences of virtual times. associatedtype VirtualTimeIntervalUnit /** Converts virtual time to real time. - parameter virtualTime: Virtual time to convert to `Date`. - returns: `Date` corresponding to virtual time. */ func convertFromVirtualTime(_ virtualTime: VirtualTimeUnit) -> RxTime /** Converts real time to virtual time. - parameter time: `Date` to convert to virtual time. - returns: Virtual time corresponding to `Date`. */ func convertToVirtualTime(_ time: RxTime) -> VirtualTimeUnit /** Converts from virtual time interval to `NSTimeInterval`. - parameter virtualTimeInterval: Virtual time interval to convert to `NSTimeInterval`. - returns: `NSTimeInterval` corresponding to virtual time interval. */ func convertFromVirtualTimeInterval(_ virtualTimeInterval: VirtualTimeIntervalUnit) -> RxTimeInterval /** Converts from virtual time interval to `NSTimeInterval`. - parameter timeInterval: `NSTimeInterval` to convert to virtual time interval. - returns: Virtual time interval corresponding to time interval. */ func convertToVirtualTimeInterval(_ timeInterval: RxTimeInterval) -> VirtualTimeIntervalUnit /** Offsets virtual time by virtual time interval. - parameter time: Virtual time. - parameter offset: Virtual time interval. - returns: Time corresponding to time offsetted by virtual time interval. */ func offsetVirtualTime(_ time: VirtualTimeUnit, offset: VirtualTimeIntervalUnit) -> VirtualTimeUnit /** This is aditional abstraction because `Date` is unfortunately not comparable. Extending `Date` with `Comparable` would be too risky because of possible collisions with other libraries. */ func compareVirtualTime(_ lhs: VirtualTimeUnit, _ rhs: VirtualTimeUnit) -> VirtualTimeComparison } /** Virtual time comparison result. This is aditional abstraction because `Date` is unfortunately not comparable. Extending `Date` with `Comparable` would be too risky because of possible collisions with other libraries. */ public enum VirtualTimeComparison { /// lhs < rhs. case lessThan /// lhs == rhs. case equal /// lhs > rhs. case greaterThan } extension VirtualTimeComparison { /// lhs < rhs. var lessThen: Bool { return self == .lessThan } /// lhs > rhs var greaterThan: Bool { return self == .greaterThan } /// lhs == rhs var equal: Bool { return self == .equal } } ================================================ FILE: Pods/RxSwift/RxSwift/Schedulers/VirtualTimeScheduler.swift ================================================ // // VirtualTimeScheduler.swift // RxSwift // // Created by Krunoslav Zaher on 2/14/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // /// Base class for virtual time schedulers using a priority queue for scheduled items. open class VirtualTimeScheduler : SchedulerType { public typealias VirtualTime = Converter.VirtualTimeUnit public typealias VirtualTimeInterval = Converter.VirtualTimeIntervalUnit private var _running : Bool private var _clock: VirtualTime fileprivate var _schedulerQueue : PriorityQueue> private var _converter: Converter private var _nextId = 0 /// - returns: Current time. public var now: RxTime { return _converter.convertFromVirtualTime(clock) } /// - returns: Scheduler's absolute time clock value. public var clock: VirtualTime { return _clock } /// Creates a new virtual time scheduler. /// /// - parameter initialClock: Initial value for the clock. public init(initialClock: VirtualTime, converter: Converter) { _clock = initialClock _running = false _converter = converter _schedulerQueue = PriorityQueue(hasHigherPriority: { switch converter.compareVirtualTime($0.time, $1.time) { case .lessThan: return true case .equal: return $0.id < $1.id case .greaterThan: return false } }, isEqual: { $0 === $1 }) #if TRACE_RESOURCES let _ = Resources.incrementTotal() #endif } /** Schedules an action to be executed immediately. - parameter state: State passed to the action to be executed. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { return self.scheduleRelative(state, dueTime: 0.0) { a in return action(a) } } /** Schedules an action to be executed. - parameter state: State passed to the action to be executed. - parameter dueTime: Relative time after which to execute the action. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { let time = self.now.addingTimeInterval(dueTime) let absoluteTime = _converter.convertToVirtualTime(time) let adjustedTime = self.adjustScheduledTime(absoluteTime) return scheduleAbsoluteVirtual(state, time: adjustedTime, action: action) } /** Schedules an action to be executed after relative time has passed. - parameter state: State passed to the action to be executed. - parameter time: Absolute time when to execute the action. If this is less or equal then `now`, `now + 1` will be used. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func scheduleRelativeVirtual(_ state: StateType, dueTime: VirtualTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { let time = _converter.offsetVirtualTime(self.clock, offset: dueTime) return scheduleAbsoluteVirtual(state, time: time, action: action) } /** Schedules an action to be executed at absolute virtual time. - parameter state: State passed to the action to be executed. - parameter time: Absolute time when to execute the action. - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ public func scheduleAbsoluteVirtual(_ state: StateType, time: Converter.VirtualTimeUnit, action: @escaping (StateType) -> Disposable) -> Disposable { MainScheduler.ensureExecutingOnScheduler() let compositeDisposable = CompositeDisposable() let item = VirtualSchedulerItem(action: { let dispose = action(state) return dispose }, time: time, id: _nextId) _nextId += 1 _schedulerQueue.enqueue(item) _ = compositeDisposable.insert(item) return compositeDisposable } /// Adjusts time of scheduling before adding item to schedule queue. open func adjustScheduledTime(_ time: Converter.VirtualTimeUnit) -> Converter.VirtualTimeUnit { return time } /// Starts the virtual time scheduler. public func start() { MainScheduler.ensureExecutingOnScheduler() if _running { return } _running = true repeat { guard let next = findNext() else { break } if _converter.compareVirtualTime(next.time, self.clock).greaterThan { _clock = next.time } next.invoke() _schedulerQueue.remove(next) } while _running _running = false } func findNext() -> VirtualSchedulerItem? { while let front = _schedulerQueue.peek() { if front.isDisposed { _schedulerQueue.remove(front) continue } return front } return nil } /// Advances the scheduler's clock to the specified time, running all work till that point. /// /// - parameter virtualTime: Absolute time to advance the scheduler's clock to. public func advanceTo(_ virtualTime: VirtualTime) { MainScheduler.ensureExecutingOnScheduler() if _running { fatalError("Scheduler is already running") } _running = true repeat { guard let next = findNext() else { break } if _converter.compareVirtualTime(next.time, virtualTime).greaterThan { break } if _converter.compareVirtualTime(next.time, self.clock).greaterThan { _clock = next.time } next.invoke() _schedulerQueue.remove(next) } while _running _clock = virtualTime _running = false } /// Advances the scheduler's clock by the specified relative time. public func sleep(_ virtualInterval: VirtualTimeInterval) { MainScheduler.ensureExecutingOnScheduler() let sleepTo = _converter.offsetVirtualTime(clock, offset: virtualInterval) if _converter.compareVirtualTime(sleepTo, clock).lessThen { fatalError("Can't sleep to past.") } _clock = sleepTo } /// Stops the virtual time scheduler. public func stop() { MainScheduler.ensureExecutingOnScheduler() _running = false } #if TRACE_RESOURCES deinit { _ = Resources.decrementTotal() } #endif } // MARK: description extension VirtualTimeScheduler: CustomDebugStringConvertible { /// A textual representation of `self`, suitable for debugging. public var debugDescription: String { return self._schedulerQueue.debugDescription } } final class VirtualSchedulerItem